synchronized
一. synchronized解读
1.1 简单描述
synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是同步,也称之为同步锁。
synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
1.2 特性
原子性:synchronized保证语句块内操作是原子的
同步方法
ACC_SYNCHRONIZED 这是一个同步标识,对应的 16 进制值是 0x0020
这 10 个线程进入这个方法时,都会判断是否有此标识,然后开始竞争 Monitor
对象。
同步代码
monitorenter,在判断拥有同步标识 ACC_SYNCHRONIZED 抢先进入此方法
的线程会优先拥有 Monitor 的 owner ,此时计数器 +1。
monitorexit,当执行完退出后,计数器 -1,归 0 后被其他进入的线程获得。
可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现)
那么为什么添加 synchronized 也能保证变量的可见性呢?
因为:
线程解锁前,必须把共享变量的最新值刷新到主内存中。
线程加锁前,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存
中重新读取最新的值。
volatile 的可见性都是通过内存屏障(Memnory Barrier)来实现的。
synchronized 靠操作系统内核的Mutex Lock(互斥锁)实现,相当于 JMM 中的 lock、unlock。退出代码块时刷新变量到主内存。
有序性:synchronized保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”)
as-if-serial,保证不管编译器和处理器为了性能优化会如何进行指令重排序,
都需要保证单线程下的运行结果的正确性。也就是常说的:如果在本线程内观察,
所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序
的。
这里有一段双重检验锁(Double-checked Locking)的经典案例:
public class SingletonDoubleCheckLock { private SingletonDoubleCheckLock(){} private volatile static SingletonDoubleCheckLock instance; public SingletonDoubleCheckLock getInstance(){ if (null == instance){ synchronized (SingletonDoubleCheckLock.class){ if (null == instance){ instance = new SingletonDoubleCheckLock(); } } } return instance; }
为什么,synchronized 也有可见性的特点,还需要 volatile 关键字?
因为,synchronized 的有序性,不是 volatile 的防止指令重排序。那如果不加 volatile 关键字可能导致的结果,就是第一个线程在初始化初始化对象,设置 instance 指向内存地址时。第二个线程进入时,有指令重排。在判断 if (instance == null) 时就会有出错的可能,因为这会可能 instance 可能还没有初始化成功。
重入性:synchronized 是可重入锁,也就是说,允许一个线程二次请求自己持有对象锁
的临界资源,这种情况称为可重入锁