面试Java基础问题汇总 part2

时间:2022-07-25
本文章向大家介绍面试Java基础问题汇总 part2,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

synchronized关键字的底层原理

Synchronized关键字的底层原理属于JVM层面。

Synchronized关键字同步语句块使用的是monitorentermonitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令指示结束位置。

当执行monitorenter指令时,线程试图获取monitor的所有权(monitor对象存在于每个Java对象的对象头中,synchronized所便是通过这种方式获取锁的,也解释了为什么Java中任意对象可以作为锁的原因)。当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行monitorexit指令后将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那么当前线程就要阻塞等待,直到锁被另外一个线程释放为止。

ThreadLocal

ThreadLocal用于实现每一个线程都有自己的专属本地变量。

如果创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本

Synchronized和ReentrantLock的区别

  1. 两个都是可重入锁。
  2. synchronzied依赖于JVM而ReentrantLock依赖于API。
  3. 由于synchronized在JDK1.6进行过大量优化,所以性能已经不是选择标准。
  4. ReentrantLocksynchronized增加了一些高级功能:
    • ReentrantLock可以通过lock.lockInterruptibly()来实现中断线程对锁的等待,改为去做其他事。
    • ReentrantLock可以指定是公平锁还是非公平锁(ReentrantLock(boolean fair));而synchronized只能是非公平锁。
    • synchronized关键字可以与wait()notify()/notifyAll方法相结合可以实现等待/通知机制。ReentrantLock也可以实现,需要借助Condition接口和newCondition()方法。线程可以注册在指定的Condition实例中从而可以有选择性的进行线程通知,在调度上更加灵活。而synchronized关键字notify()方法被通知的线程是由JVM选择的,执行notifyAll()方法时,相当于整个Lock对象中只有Condition实例,所有的线程都注册在它一个身上,notifyAll()方法会通知所有处于等待的线程,这样造成很大的效率问题,而Condition实例的singal()方法只会唤醒注册在该Condition实例上的线程。

volatile关键字

JDK1.2之前,Java的内存模型实现总是从主存(即共享内存中)读取变量,是不需要进行特别注意的。而在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器寄存器)中,而不是直接在主存中进行读写。这就可能造成了一个线程在主存中修改了一个变量的值,而另外一个线程还在继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。为了解决这个问题,我们可以声明变量为volatile,这指示JVM,这个变量是不稳定的,每次使用它都在主存中进行读取。

volatile关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排。

并发编程的三个重要特性

  1. 原子性:一个代码片段,要么所有的操作全部得到执行并且不会受到任何因素的干扰而中断,要么全部都不执行。使用synchronized可以保证代码片段的原子性。
  2. 可见性:当一个变量对共享变量进行了修改,那么另外的线程都是立即能看到修改后的最新的值。votaile关键字可以保证共享变量的可见性。
  3. 有序性:代码在执行过程中的先后顺序,Java在编译器以及运行期间的优化,代码的执行顺序未必的就是编写代码时候的顺序。volatile关键字可以禁止指令进行重排序优化。

Synchronized关键字和Volatile关键字的区别

  • volatile关键字是线程同步的轻量级实现,所以volatile关键字性能肯定要比synchronized关键字性能要好。
  • volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。 (Java SE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级所以及其他各种优化之后执行效率有显著提升,实际开发过程中使用synchronized关键字的场景还是更多一些。)
  • 多线程访问volatile关键字不会发生阻塞,而synchronized关键字两者都能保证。
  • volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程访问资源的同步性。

sleep()和wait()的共同点和不同点

  • 两者都可以暂停线程执行。
  • sleep()没有释放锁,而wait()方法释放了锁。
  • wait()通常用于线程间交互/通信,sleep()通常被用于暂停执行。
  • wait()方法调用后,线程不会自动苏醒,需要别的线程调用同一对象的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒,或者可以使用wait(long timeout)超时后线程会⾃动苏醒。

为什么我们调⽤ start() ⽅法时会执⾏ run() ⽅法,为什么我们不能直接调⽤run() ⽅法?

start()方法会执行线程的相应准备工作,然后再执行run ()方法的内容,这是真正的多线程工作,而直接运行run(),会把run()方法当作主线程下的普通方法执行,并不会在某个线程执行它,所以不是多线程工作。

总结: 调⽤ start() ⽅法⽅可启动线程并使线程进⼊就绪状态,⽽ run() ⽅法只是 thread 的⼀个普通⽅法调⽤,还是在主线程⾥执⾏。

双重校验所实现单例模式(线程安全)

单例模式是Java中最简单的设计模式之一,它属于创建性模式,提供了一种创建对象的最佳方式。

简单地说,单例模式涉及到一个单一的类,且该类负责自己创建自己的对象,同时需要确保只有单个对象被创建,并且提供了访问其唯一对象的方式,可以直接访问到,不需要实例化该类的对象

public class Singleton {
	private volatile static Singlention uniqueInstance;
	private Singleton() {}
	public synchronized static Singleton getUniqueInstance() {
		if(uniqueInstacne == null) {
			synchronized (singleton.class) {
				if(uniqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
}

volatile关键字可以禁止JVM指令重排,保证多线程环境下也能正常运行。

synchronized 关键字的底层原理

synchronized 关键字底层原理属于 JVM 层⾯。 ① synchronized 同步语句块的情况 synchronized 同步语句块的实现使⽤的是monitorentermonitorexit 指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置 ② synchronized 修饰⽅法的的情况 synchronized修饰的方法并没有monitorenter指令和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标识,该标识指明了该方法是一个同步方法,JVM通过该ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法,从而执行相应的方法调用。

JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍⼀下这些优

化吗?

这里做个引子,需要详细去了解,面试中很有可能的问题是,你了解java的锁吗,请介绍一下?

偏向锁、轻量级锁、自选锁、适应性自选锁、锁消除、锁粗化等减少锁操作的开销。

锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。锁之呢个升级不能降级,这种策略是为了提高获得锁和释放锁的效率。

Synchronized 和 ReentrantLock的区别

  1. 两者都是可重入锁。
  2. synchronized依赖于JVMReentrantLock依赖于API
  3. ReentrantLocksynchronized增加了一些高级功能。主要有三点:(1)等待可中断;(2) 可实现公平锁;(3)可选择性通知(锁可以绑定多个Condition)
  4. 性能不是选择标准。

TCP、UDP协议的区别

类型

面向连接

传输可靠性

传输形式

传输效率

所需资源

应用场景

首部字节

TCP

面向连接

可靠

字节流

要求通讯数据可靠(文件传输、邮件传输)

20-60

UDP

无连接

不可靠

数据报文段

要求通信速度快(域名转换)

8个字节

TCP提供面向连接的服务。数据传送之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或者多播服务。

TCP的可靠性体现在传递数据之前,会有三次握手来建立连接,数据传递时,有确认、窗口、重传、拥塞控制机制,数据传送完毕后,还有断开链接。

TCP协议如何保证可靠传输 (重点)

  1. 应用数据被分割成TCP最适合发送的数据块。
  2. TCP给发送的每一个包进行编号,接收方对数据包进行排序,将有序数据传送给应用层。
  3. 校验和:TCP将保持它首部和数据的校验和。这是一个端到端的检验和,目的时检测数据在传输过程中的任何变化。如果校验和有差错,TCP将丢弃这个报文段并不确认收到此报文。
  4. TCP的接收端会丢弃重复的数据。
  5. 流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端能接纳的数据。当接受方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。
  6. 拥塞控制:当网络拥塞时,减少数据的发送。
  7. 停止等待协议:也是为了实现可靠传输的,它的基本原理就是每发完一个分组就- 停止发送,等待对方确认。在收到确认后再发下一个分组。 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段(超时重传)。

ARQ协议:也是为了实现可靠传输,基本原理时每发完一个分组就停止发送,等待对方确认。收到确认后再发下一个分组。

  • 停止等待ARQ协议
  • 连续ARQ协议

⾃动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层和传输层的错误纠正协议之⼀。它通过使⽤确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送⽅在发送后⼀段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议。

在浏览器输入一个url地址到显示页面的过程

  1. 浏览器查找域名的IP地址(DNS查找过程、浏览器缓存、路由器缓存、DNS缓存)。【对应DNS协议:获取域名对应IP】
  2. 浏览器向web服务器发送一个HTTP请求(该过程中cookies会随着请求发送给服务器)。
  3. 服务器处理请求(请求、处理请求及其参数、cookies,生成一个HTML相应)。
  4. 服务器返回一个HTML响应。
  5. 浏览器开始显示HTML。

2-5 包含协议: TCP:与服务器建立连接 IP:建立TCP协议时,需要发送数据,在网络层使用IP协议 OSPF:IP数据包在路由器之间,路由器选择使用OSPF协议(类似协议有RIP协议、IGRP(cisco私有)、EIGRP(cisco私有) ARP:路由器在与服务器通信时,需要将IP地址转化为MAC地址,该过程使用ARP协议 HTTP:TCP连接建立完成后,使用HTTP协议访问网页

  1. DNS解析
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染画面
  6. 连接结束

状态码

有些公司会问的很细,比如505是什么意思?

状态码

类别

原因短语

1xx

Information(信息性状态码)

接收的请求正在处理

2xx

Success(成功状态码)

请求正常处理完毕

3xx

Redirection(重定向状态码)

需要附加操作以完成请求

4xx

Clien Error(客户端错误状态码)

服务器无法处理请求

5xx

Server Error(服务器错误状态码)

服务器处理请求出错

HTTP是不保存状态的协议,如何保存用户状态?

使用Session和Cookies。

Session和Cookies的区别

Cookies和Session都是用来跟踪浏览器用户身份的会话方式,应用场景不太一样。

Cookies一般用来保存用户信息: (1)保存上次登录信息,下次自动填充; (2)下次访问不需要重新登陆; (3)登录一次网站后访问同网站其他页面不需要重新登录。

Session主要作用通过服务端记录用户的状态(典型场景购物车)。

Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。 Cookie 存储在客户端中,⽽Session存储在服务器上,相对来说 Session 安全性更⾼。如果要在Cookie 中存储⼀些敏感信息,不要直接写⼊ Cookie 中,最好能将 Cookie 信息加密然后使⽤到的时候再去服务器端解密。

HTTP1.0和HTTP1.1的主要区别

  1. 长连接:HTTP/1.0使用短链接,每次请求都要建立一次连接;HTTP/1.1使用长连接,默认开启Conection: keep-alive。持续连接分为流水线方式和非流水线方式。流水线是指,客户端在收到HTTP响应报文前就能接着发送新的请求报文;非流水线则是指客户端收到响应之后才能发送下一个请求。
  2. HTTP/1.1新增了24个错误状态响应码。如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  3. 缓存处理:HTTP/1.1新增了更多的缓存头来控制缓存控制策略。
  4. 带宽优化及网络连接的使用:HTTP/1.1允许值请求资源的某个部分,以此节省带宽。

HTTP和HTTPS的区别

  1. 端口:HTTP使用80,HTTPS使用443。
  2. 安全性和资源消耗:HTTP运行在TCP之上,所有传输的都是明文,客户端和服务器无法验证对方身份。HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,传输内容经过对称加密,但对称加密的密匙用服务器方的证书进行了非对称加密。
  • 对称加密:密匙只有一个,加密解密为同一个密码;
  • 非对称加密:密匙成对出现(分为公匙和私匙,且根据公匙无法推知私匙,加密解密使用不同密匙)。

参考资料

JavaGuide面试突击版,百度可得最新版。这里修正了原文的一些错误和不严谨的地方。