面试javamysql计算机网络操作系统

字节 客户端 1面

算法题

  • 给定一个数组,从中取3个数组成周长最大的三角形

    如果满足条件,就是答案;如果不满足,其他任意两边也不满足,前移动

    class solve {
      public static void main(String[] args) {
        int[] arr = [4,5,6,2,1,2,3,7];
        // 先排序 
        Arrays.sort(arr);
        int ans = 0;
        for (int i = n - 3; i >= 0; i--) {
          if (arr[i] + arr[i + 1] > a[i + 2]) {
            ans = a[i] + a[i+1] + a[i + 2];
            break;
          }
        }
        System.out.println(ans);
      }
    }

进程和线程的联系

进程是资源分配(打开的文件,分配的内存)的最小单位,线程依赖于进程,是任务执行的最小单位。一个进程可以有多个线程,多个线程共享进程的堆和方法区(JDK1.8之后的元空间)

  • 为什么要使用线程?

    以java为例,没使用多线程时,所有代码都是在**main()**线程中执行。现在假设有A,B,C三个任务,A是一个耗时任务(比如IO操作),此线程会挂起等待IO完成并且让出CPU,IO结束再运行,这样B任务要等到A任务完成,C任务又要等到B任务(假设也耗时)完成。

    如果使用多线程,可以为两个耗时任务A,B开启线程执行,即使A,B耗时和C的运行也不受影响。

  • 线程共享哪些资源?

    打开的文件,分配的内存

  • 进程的调度机制?

    FCFS(先来先服务)SJF(短作业优先)优先级调度

    时间片轮转调度:时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称 RR(Round robin)调度。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。

    多级反馈队列调度:多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。

MySQL的事务

事务是对数据库的逻辑上的一组操作,要求保持以下4个特点(ACID):

以银行转账举例:

START TRANSACTION;
SELECT balance FROM checking WHERE customer_id = 10233276;
UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;
UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;
COMMIT;
  • 原子性:不可分割的最小工作单元,整个事务中的所有操作,要么全部成功,要么全部失败回滚
    • 事务提交后要么(checking余额 -200,saving余额+200),要么都不执行。
  • 一致性:事务执行后,数据库总是从一个一致性(正确的)状态转换到另一个一致性(正确的)状态
    • 在第3行之后,第4行之前系统奔溃,200不会减,因为事务还没提交。
  • 隔离性:通常来说,一个事务所做的修改在最终提交之前,对其他事务不可见。
    • 在第3行之后,第4行之前,另一个事务去查看checking,余额不会变,因为事务还没提交。
  • 持久性:一旦事务提交,其所做的修改就会永远保存到数据库中。

并发事务带来的问题:

  • 更新丢失:两个事务同时更新某一行,先提交的事务的更新被后提交的事务的更新覆盖。

  • 脏读:一个事务正在修改某一行,在事务提交前,该行处于不一致状态;如果另一条事务同时来读数据,就会读到“脏”数据。

  • 幻读不可重复读

    • 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读” 。

    • 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读” 。

      幻读和不可重复读的区别在于:幻读读出的记录数不一样(因为其他事务的新增和删除);不可重复读读出的数据不一样(因为其他事务的修改)。

  • 解决方法:加锁,数据多版本并发控制(MVCC,多版本数据库)根据时间点生成一致性数据快照(Snapshot)

TCP和UDP区别?

  • 传输控制协议 TCP(Transmission Control Protocol)--提供面向连接的,可靠的数据传输服务。

  • 用户数据协议 UDP(User Datagram Protocol)--提供无连接的,尽最大努力的数据传输服务(不保证数据传输的可靠性)。

TCP如何保证可靠传输?

应用数据被分割成TCP认为合适的数据块,编号校验和TCP接收端丢弃重复数据流量控制

拥塞控制:拥塞控制可以防止数据过多地发送到网络中,避免路由器或连路过载。通信双方本身并不能了解到拥塞的细节,只能根据通信时延的增加来确认发生了拥塞。

  • 慢开始: 拥塞窗口初始为 1,每收到一个确认,则加 1(加法增大);从时间上看,每个单位时间都扩大一倍(指数增加,$2^n$);增加存在上限ssthresh,当达到慢启动门限后,就会使用拥塞避免算法。
  • 拥塞避免: 没收到cwnd个 ACK,则加 1(线性增长,每个单位时间加 1);当出现超时时(发生拥塞),则设置ssthresh=cwnd/2cwnd=1
  • 快重传: 连续收到 3 个冗余确认,直接重传丢失的包,不等待计时器。
  • 快恢复: 在快重传发生后,cwnd=ssthresh=cwnd/2,返回拥塞避免状态。

流量控制:TCP 提供基于滑动窗口协议的流量控制,实现了端到端的流量控制。

滑动窗口:由于传输本身存在时延,一问一答式的传输效率极低,更好的办法应该是同时发送很多组数据。发送方和接收方各维护一个窗口(发送窗口和接收窗口)

发送窗口包含 4 部分

  • 已发送成功的部分: 这部分数据是已经被接收方确认接受过
  • 已发送但未确认的部分: 这部分数据已经发送,但暂时未收到确认
  • 未发送但接收方可处理的部分: 这部分数据还未发送,但仍然在发送窗口范围内,将会继续发送
  • 未发送且接收方不可处理的部分: 这部分数据在窗口外,暂时不需要发送

通过冗余 ACK 机制,可以对已接收到的一组数据同时返回确认

接收窗口包含 3 部分

  • 已确认接收的部分
  • 未接受但可接受的部分
  • 不可接受的部分

当接收窗口接收到超出窗口范围的数据,将会被直接丢掉。而接收到数据,将会被标记为已接收成功。每次确认会将第一个未被确认的报文发送回去。

发送窗口和滑动窗口并不是一成不变的,两者近似相等,但是由于网络问题以及机器处理速度,实际上可能会稍有变动。接收窗口通过首部的相应字段告知发送方,而发送窗口将根据对方的接收窗口以及网络拥塞窗口共同确认。

拥塞控制是全局性的过程,涉及所有相关主机和路由器

流量控制是端到端的控制,通过抑制发送端的速率来确保接收端有能力全部接收

HTTP的请求方法

| 注解 | 描述 | | ------- | ------------------------------------------------------------ | | GET | GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据。 | | HEAD | HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体。 | | POST | POST方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。 | | PUT | PUT方法用请求有效载荷替换目标资源的所有当前表示。 | | DELETE | DELETE方法删除指定的资源。 | | CONNECT | CONNECT方法建立一个到由目标资源标识的服务器的隧道。 | | OPTIONS | OPTIONS方法用于描述目标资源的通信选项。 | | TRACE | TRACE方法沿着到目标资源的路径执行一个消息环回测试。 | | PATCH | PATCH方法用于对资源应用部分修改。 |

HTTP断点续传

有时用户上传/下载文件需要历时数小时,万一线路中断,不具备断点续传的 HTTP/FTP 服务器或下载软件就只能从头重传,比较好的 HTTP/FTP 服务器或下载软件具有断点续传能力,允许用户从上传/下载断线的地方继续传送,这样大大减少了用户的烦恼。

Range & Content-Range

HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。

Range

用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:

Range:(unit = first byte pos)-[last byte pos]

Content-Range

用于响应头中,在发出带 Range 的请求后,服务器会在 Content-Range 头部返回当前接受的范围和文件总大小。一般格式:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]

int 和 integer的区别

Java有两种数据类型:基本数据类型(int, char, byte, boolean, float, double等)和引用数据类型,分为数组,类,接口。

Java为了能够将这些基本数据类型当成对象操作,为每 一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

// 基本数据类型
booleancharbyteshortintlongfloatdouble
// 包装类类型
BooleanCharacterByteShortIntegerLongFloatDouble

区别:

  • Integer是int的包装类;int是基本数据类型;
  • Integer变量必须实例化后才能使用;int变量不需要;
  • Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
  • Integer的默认值是null;int的默认值是0。

通过new生成的两个对象是永远不相等的(内存地址不同)。

Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)

Integer a = new Integer(100);
int b = 100;
System.out.print(a == b); //true

非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。因为非new生成的Integer变量指向的是静态常量池中cache数组中存储的指向了堆中的Integer对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的对象引用(地址)不同。

Integer a = new Integer(100);
Integer b = 100;
System.out.print(a == b); //false

对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间**-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false**。

Integer a = 100;
Integer b = 100;
System.out.print(a == b); //true

Integer a = 128;
Integer b = 128;
System.out.print(a == b); //false

对于第4条的原因: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100)。而java API中对Integer类型的valueOf的定义如下,对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127这个Integer对象进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。

Java中的voliate关键字

并发编程中的三个概念

  • 原子性(类似于mysql事务的原子性)

  • 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  • 有序性:即程序执行的顺序按照代码的先后顺序执行。

    多线程访问同一变量时一般会导致可见性问题,JVM在执行一段有序代码时可能会发生指令重排导致有序性问题。

并发程序要能正确执行,必须保证三个性质缺一不可。

Java内存模型规定所有的变量都是存在主存当中(物理内存),每个线程都有自己的工作内存(高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。执行线程必须先在自己的工作线程中对变量所在的缓存行进行赋值操作,然后再写入主存当中。

在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。

voliate关键字用于保证可见性

当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去物理内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时物理内存中可能还是原来的旧值,因此无法保证可见性。

另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

参考:


发布于2021-03-22 22:40:31