- 对象锁
- 普通方法锁:默认锁对象为this,当前实例对象
- 同步代码块锁:自己指定的锁对象,可以为this,也可以为某一个实例对象
- 当两个线程使用不同对象进行加锁时,互不影响
- ConcurrentHashMap
- 类锁
- 静态方法锁:默认锁对象为Class类对象
- 同步代码块指定锁对象为 XXX.class
- 对象锁和类锁互不影响
- Singleton
- 同步锁特性:
- 互斥同步(CAS使用非阻塞同步)
- JVM自动使用moniter加锁和解锁
- 可重入
- 不可中断
- 同步锁缺点:
- 效率低:锁的释放情况少,试图获得锁时不可设定超时,不能中断一个试图获得锁的线程
- 不够灵活:加锁和释放锁的时机单一,每个锁仅有一个单一的条件
- 注意事项
- 锁对象不能为空
- 作用域范围不宜过大
- 使用时避免死锁
- JDK6对synchronized关键字的优化
- 自旋锁:JDK5及之前,同步锁都是重量级锁,线程阻塞和唤醒需要操作系统将线程在用户态和内核态来回切换完成,本身非常耗时。JDK6之后默认开启自旋锁,当锁发生竞争时,没有获得该锁的线程不会立刻被阻塞,可以循环一段时间(自旋),以便当锁释放后迅速拿到锁;
- 偏向锁:为了解决在锁无竞争状态下,某个一直使用该锁的线程,在下次进入锁相关的代码块时,虚拟机无须进行加锁、释放锁、同步等操作;
- 轻量级锁:为了解决在锁无竞争状态下,多个线程依次访问时使用CAS操作,避免使用互斥量的开销。如果两个线程以上竞争该锁,将膨胀为重量级锁
- 锁消除:如果变量不会逃逸,对对象的同步锁,JVM会消除;对于下面的append方法,JVM会移除对sb的加锁、释放锁
public String addStrings(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
- 锁粗化:如果一些代码零零碎碎对同一个对象加锁,JVM会加大锁的范围,避免多次加锁、解锁
- synchronized和ReentrantLock对比(参考synchronized缺点)
- ReentrantLock等待可中断
- ReentrantLock可以指定是公平锁还是非公平锁
- ReentrantLock可以绑定多个条件
- ReentrantLock必须要手动释放锁