https://www.imooc.com/video/23597
1. synchronized的作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
2. synchronized 的两种用法
2.1 对象锁
包括方法锁(默认锁对象为this当前实例对象)和同步代码块(自己指定锁对象)
public synchronized void add() {
i++;
}
public void add() {
synchronized (this) {
i++;
}
}
Object obj = new Object();
public void add() {
synchronized (obj) {
i++;
}
}
2.2 类锁
值synchronized修饰静态的方法或指定锁为Class对象
- Java类可能有很多个对象,但只有一个Class对象
public static synchronized void add() {
i++;
}
特别要注意:类锁是Class对象,是唯一的,不同对象间可以形成锁等待。public static void add() {
synchronized (Demo2.class){
i++;
}
}
总结: 对于同一把锁,同一时刻只能被一个线程持有,其他线程要等待锁释放。
注意:线程执行方法抛出异常,会自动释放持有的锁。
3. synchronized的性质
3.1 可重入性
- 指的是同一线程的外层函数获得锁以后,内层函数可以直接再次获取该锁。
-
3.2 不可中断性
一旦这个锁已经被别的线程获得,如果我还想获得,我只能选择等待或者阻塞,知道别的线程释放这个锁。如果别的线程永远不释放锁,那么我只能永远地等下去。 简单一句话:synchronized会死等,直到得到锁。
4. synchronized的原理
4.1 通过字节码查看加锁和释放锁的原理
获取和释放锁的时机:进入和退出绒布代码块(包括抛出异常)
- 对于ReentryLock,我们要自己控制加锁和释放锁的时机
synchronize在字节码中会别转为以下指令
可重入原理:加锁次数计数器
- JVM会记录被加锁的次数
- 第一次加锁时,次数从0变为1,之后如果再次加锁,就从1变为2,以此类推。
退出一层同步代码块时,计数减一,当计数为0的时候,代表锁释放
4.3 synchronized 可见性
-
5. synchronized的缺陷
效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程。
- 锁的释放情况少,指线程执行完同步代码块或执行过程中抛出了异常才能释放锁。
- 试图获得锁时不能设定超时,也就是说会一直试图获取锁
- synchronized
- 不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能不够的。
- 无法知道是否成功获取到锁
使用注意点:锁的范围不宜过大,避免锁的嵌套(锁的嵌套可能会形成死锁)。