Synchronized原理及升级
一、synchronized可以解决可见性、原子性、有序性
1.可见性:synchronized对一个类或对象加锁时必须先获得锁,而锁的状态其他线程是可见的,并且在释放锁之前会将对变量的修改刷新到内存中,保证变量是可见的。
2.原子性:因为在执行 之前,类或对象先要获取锁,直到执行完才能释放。也就是一次只有一个线程执行,所以一个线程的执行不受干扰,保证原子性。
3.有序性:java编译器和处理器会对指令进行重排,但指令重排不会影响单线程的顺序,他影响的是多线程的顺序,而synchronized保证每个时刻只有一个线程执行,所以可以保证有序性。
二、synchronized三种使用方式:
1.修饰实例方法:给当前实例对象加锁
2.修饰静态方法:给当前类对象加锁
3.修饰代码块:给指定对象或类加锁
三、synchronized实现原理:
从底层指令角度来看,synchronized是基于monitor监视器来实现的,也叫做管程。
monitor是为了解决操作系统级别线程同步原语的复杂性。
每个对象都存在与之关联的monitor,monitor一次只有一个线程能拥有,所以是线程私有的数据结构。
通过将monitorenter指令插入在编译后的同步代码块的开始位置,将monitorexit指令插入到异常和结束位置,当一个线程常识获取锁对象时,如果monitor对象为0,意味着当前monitor还未被获取,这个线程就会立刻拥有,然后将锁计算器+1。对象的锁计数器>0,则其他线程再想获取该对象锁,就需要等待EntryLIst等待,直到锁释放。如果该线程已经拿到了monitor,又重入这把锁,计数器就会累加。当该线程调用wait方法,则进入waitset队列等待唤醒,如果唤醒执行退出后就会释放monitor。
四、锁升级
JDK1.6后对synchronized进行升级,引入偏向锁和轻量级锁来提高执行效率。锁的状态有四种:无状态锁、偏向锁、轻量级锁、重量级锁。锁可以升级但不能降级。
1.偏向锁:
偏向锁的依据是对于绝大部分锁,在整个同步周期内不仅不存在竞争,而且总是由同一线程多次获得。
偏向锁获取过程:线程第一次获取锁对象的时候,JVM会将锁对象的对象头中的锁状态设置为偏向锁,同时记录线程的id,持有偏向锁的线程每次进入或退出时,只需要判断当前对象头中是否存在该线程id,如果是则直接访问。
当另一个线程也要获取锁对象时,就撤销该偏向锁,升级为轻量级锁。
2.轻量级锁:
通过CAS操作获取锁对象,如果CAS自旋次数过多,说明竞争激烈则会升级为重量级锁。
3.重量级锁:
monitor监视器本质是调用操作系统底层互斥信号量实现。操作系统需要从用户态切换到内核态,保存线程上下文信息,执行完成后再恢复上下文信息,转换成本很高,所以synchronized效率低。
-----------------------------------------------------------------------------------------------
参考原文:https://www.nowcoder.com/discuss/353148565528715264?sourceSSR=search、