synchronized详解
一、应用
-
修饰实例方法,锁是实例对象
-
修饰静态方法,锁就是当前类的 class 对象锁
- 静态成员不专属于任何一个实例对象,是类成员,因此通过 class 对象锁可以控制静态成员的并发操作。一个线程 A 调用一个实例对象的非 static synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的 class 对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁,二者的锁并不一样,所以不冲突。
-
修饰代码块,锁对象就是放在括号里面的对象;
-
锁对象是当前实例
synchronized(this) { for (int j = 0; j < 100; j++) { i++; } }
-
锁对象是类的 class 对象锁
synchronized(AccountingSync.class) { for (int j = 0; j < 100; j++) { i++; } }
-
**在jdk1.6之后,synchronized的内部进行了优化,它不再是一个简单的重量级锁,它为了试用所有的情况,有了一个锁升级流程: 偏向锁 -》 轻量级锁 -》 重量级锁,接下来我们仔细的聊一下所谓的锁升级流程。synchronized是自旋锁 **
重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。 不要立即进行自旋。JVM第四章5.3
二、可重入锁
可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。(同一个加锁线程自己调用自己不会发生死锁情况)
synchronized 和 ReentrantLock 都是可重入锁。
synchronized的可重入怎么实现
每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁并调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。