什么是synchronized
?
JVM会自动通过使用
monitor
来加锁和解锁,保证了同时只有一个线程可以执行指令代码,从而保证线程安全,具有可重入和不可中断的性质。
Synchronized的作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发的安全效果。
Synchronized的两个用法:
- 对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
- 类锁:指synchronized修饰静态的方法或指定锁为Class对象。
MONITOR状态:正在等待synchronized锁
所谓类锁,不过是Class对象的锁而已。类锁只能在同一时刻被一个对象拥有。
什么是可重入
指的是同一线程的外层函数获得锁,内层函数可以直接再次获得该锁。
synchronized性质:不可中断
一旦这个锁已经被别人获得了,如果我还想获得,我就只能选择等待或者阻塞,直到彼得线程释放这个锁。如果别人永远不释放锁,那么我就只能永远等下去。
加锁和释放锁的原理:
获取和释放锁的时机:进入和退出同步代码块(包括抛出异常)monitorenter和monitorexit指令。
可重入锁的好处:
- 1.避免死锁;
- 2.提升封装性;
可重入锁原理
- JVM会记录被加锁的次数
- 第一次加锁时,次数从0变为1,之后如果再次加锁,就从1变成2,以此类推。
- 退出一层同步代码块时,计数器减一,当计数器为0的时候,代表锁释放。
可见性
- 一个线程执行的结果,另外一个线程不一定可见。
- 线程1操作x=5,之后线程2可能读取x=3(之前的值);
- synchronized可以保证可见性。
synchronized
的缺陷
- 效率低:锁的释放情况少、试图获取锁的时候不能设置超时、不能中断一个正在试图获得锁的线程。
- 不够灵活(读写锁更灵活):加锁和释放锁的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的。
- 无法知道石佛偶成功获取锁。
使用Synchronized的注意点:
- 锁的范围不宜过大,避免锁的嵌套(容易导致死锁);
如何选择Lock和Synchronized关键字?
优先使用Synchronized
Synchronized使用时只有一个线程可以执行,性能比较差,有什么办法提升性能?
读多写少建议使用读写锁
ReentrantReadWriteLock
。
更灵活地控制锁的获取和释放
Lock
多线程访问同步方法的7种情况
- 两个线程同时访问
一个对象
的同步方法 - 两个线程同时访问
两个对象
的同步方法。 - 两个线程同时访问的是synchronized的
静态
方法 - 同时访问
同步
方法与非同步
方法 - 访问同一个对象的
不同的
普通同步方法 - 同时访问
静态
synchronized和非静态
synchronized方法 - 方法抛
异常
后,会释放锁
。