Synchronized
关键字的底层原理属于JVM层面。
Synchronized
关键字同步语句块使用的是monitorenter
和monitorexit
指令,其中monitorenter
指令指向同步代码块的开始位置,monitorexit
指令指示结束位置。
当执行monitorenter
指令时,线程试图获取monitor的所有权(monitor对象存在于每个Java对象的对象头中,synchronized所便是通过这种方式获取锁的,也解释了为什么Java中任意对象可以作为锁的原因)。当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行monitorexit指令后将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那么当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
ThreadLocal
用于实现每一个线程都有自己的专属本地变量。
如果创建了一个ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的本地副本
synchronzied
依赖于JVM而ReentrantLock
依赖于API。synchronized
在JDK1.6进行过大量优化,所以性能已经不是选择标准。ReentrantLock
比synchronized
增加了一些高级功能: 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实例上的线程。JDK1.2之前,Java的内存模型实现总是从主存(即共享内存中)读取变量,是不需要进行特别注意的。而在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器寄存器)中,而不是直接在主存中进行读写。这就可能造成了一个线程在主存中修改了一个变量的值,而另外一个线程还在继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。为了解决这个问题,我们可以声明变量为volatile,这指示JVM,这个变量是不稳定的,每次使用它都在主存中进行读取。
volatile
关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排。
synchronized
可以保证代码片段的原子性。votaile
关键字可以保证共享变量的可见性。volatile
关键字可以禁止指令进行重排序优化。volatile
关键字是线程同步的轻量级实现,所以volatile
关键字性能肯定要比synchronized
关键字性能要好。volatile
关键字只能用于变量而synchronized
关键字可以修饰方法以及代码块。
(Java SE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级所以及其他各种优化之后执行效率有显著提升,实际开发过程中使用synchronized关键字的场景还是更多一些。)volatile
关键字不会发生阻塞,而synchronized
关键字两者都能保证。volatile
关键字能保证数据的可见性,但不能保证数据的原子性。synchronized
关键字两者都能保证。volatile
关键字主要用于解决变量在多个线程之间的可见性,而synchronized
关键字解决的是多个线程访问资源的同步性。sleep()
没有释放锁,而wait()
方法释放了锁。wait()
通常用于线程间交互/通信,sleep()通常被用于暂停执行。wait()
方法调用后,线程不会自动苏醒,需要别的线程调用同一对象的notify()
或者notifyAll()
方法。sleep()
方法执行完成后,线程会自动苏醒,或者可以使用wait(long timeout)
超时后线程会⾃动苏醒。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 关键字底层原理属于 JVM 层⾯。
① synchronized 同步语句块的情况
synchronized 同步语句块的实现使⽤的是monitorenter
和monitorexit
指令,其中monitorenter
指令指向同步代码块的开始位置,monitorexit
指令则指明同步代码块的结束位置
② synchronized 修饰⽅法的的情况
synchronized修饰的方法并没有monitorenter
指令和monitorexit
指令,取而代之的是ACC_SYNCHRONIZED
标识,该标识指明了该方法是一个同步方法,JVM
通过该ACC_SYNCHRONIZED
访问标志来辨别一个方法是否声明为同步方法,从而执行相应的方法调用。
化吗?
这里做个引子,需要详细去了解,面试中很有可能的问题是,你了解java的锁吗,请介绍一下?
偏向锁、轻量级锁、自选锁、适应性自选锁、锁消除、锁粗化等减少锁操作的开销。
锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。锁之呢个升级不能降级,这种策略是为了提高获得锁和释放锁的效率。
synchronized
依赖于JVM
而ReentrantLock
依赖于API
。ReentrantLock
比synchronized
增加了一些高级功能。主要有三点:(1)等待可中断;(2) 可实现公平锁;(3)可选择性通知(锁可以绑定多个Condition)类型 | 面向连接 | 传输可靠性 | 传输形式 | 传输效率 | 所需资源 | 应用场景 | 首部字节 |
---|---|---|---|---|---|---|---|
TCP | 面向连接 | 可靠 | 字节流 | 慢 | 快 | 要求通讯数据可靠(文件传输、邮件传输) | 20-60 |
UDP | 无连接 | 不可靠 | 数据报文段 | 快 | 少 | 要求通信速度快(域名转换) | 8个字节 |
TCP提供面向连接的服务。数据传送之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或者多播服务。
TCP的可靠性体现在传递数据之前,会有三次握手来建立连接,数据传递时,有确认、窗口、重传、拥塞控制机制,数据传送完毕后,还有断开链接。
ARQ协议:也是为了实现可靠传输,基本原理时每发完一个分组就停止发送,等待对方确认。收到确认后再发下一个分组。
⾃动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层和传输层的错误纠正协议之⼀。它通过使⽤确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送⽅在发送后⼀段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议。
2-5 包含协议: TCP:与服务器建立连接 IP:建立TCP协议时,需要发送数据,在网络层使用IP协议 OSPF:IP数据包在路由器之间,路由器选择使用OSPF协议(类似协议有RIP协议、IGRP(cisco私有)、EIGRP(cisco私有) ARP:路由器在与服务器通信时,需要将IP地址转化为MAC地址,该过程使用ARP协议 HTTP:TCP连接建立完成后,使用HTTP协议访问网页
有些公司会问的很细,比如505是什么意思?
状态码 | 类别 | 原因短语 |
---|---|---|
1xx | Information(信息性状态码) | 接收的请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 需要附加操作以完成请求 |
4xx | Clien Error(客户端错误状态码) | 服务器无法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |
使用Session和Cookies。
Cookies和Session都是用来跟踪浏览器用户身份的会话方式,应用场景不太一样。
Cookies一般用来保存用户信息: (1)保存上次登录信息,下次自动填充; (2)下次访问不需要重新登陆; (3)登录一次网站后访问同网站其他页面不需要重新登录。
Session主要作用通过服务端记录用户的状态(典型场景购物车)。
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。 Cookie 存储在客户端中,⽽Session存储在服务器上,相对来说 Session 安全性更⾼。如果要在Cookie 中存储⼀些敏感信息,不要直接写⼊ Cookie 中,最好能将 Cookie 信息加密然后使⽤到的时候再去服务器端解密。
Conection: keep-alive
。持续连接分为流水线方式和非流水线方式。流水线是指,客户端在收到HTTP响应报文前就能接着发送新的请求报文;非流水线则是指客户端收到响应之后才能发送下一个请求。
JavaGuide面试突击版,百度可得最新版。这里修正了原文的一些错误和不严谨的地方。