synchronized 、volatile 以及 ReentrantLockReentrantLock 的区别
一、synchronized 和 volatile 的区别
synchronized 关键字和 volatile 关键字是两个互补的存在。
- volatile 关键字是线程同步的轻量实现,所以 volatile 性能比 synchronized 关键字要好。但是 volatile 关键字只能用于修饰变量,而 synchronized 关键字可用于修饰方法以及代码块。
- volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都可以保证。
- volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。
二、synchronized 和 ReentrantLock 的区别
2.1 ReentrantLock 是什么?
ReentrantLock 实现了 Lock 接口,是一个可重入且独占式的锁,和 synchronized 关键字类似。但是 ReentrantLock 更灵活、更强大,增加了轮询、超时、中断、公平锁和非公平锁等高级功能。
2.2 公平锁和非公平锁
- 公平锁:
锁被释放之后,先申请的线程先得到锁。性能较差一些,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁。
- 非公平锁:
锁被释放之后,后申请的线程可能会先获获取到锁,是随机或者按照其他优先级排序的。性能更好,但可能会导致某些线程永远无法获取锁。
2.3 synchronized 和 ReentrantLock
- 两者都是可重入锁
可重入锁也叫递归锁,指的是线程可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果是不可重入锁,就会造成死锁。JDK 提供的所有 Lock 的实现类,包括 synchronized 关键字锁都是可重入的。
例如,下面代码中,method1() 和method2() 都被 synchronized 关键字修饰,method1() 调用了 method2()。
public class SynchronizedDemo {public synchronized void method1() {System.out.println("方法1");method2();}public synchronized void method2() {System.out.println("方法2");}
}
由于 synchronized 锁是可重入的,同一个线程在调用 method1() 时可以直接获取当前对象的锁,执行 method2() 的时候可以再次获取这个对象的锁,不会产生死锁问题。如果 synchronized 是不可重入锁,由于该对象的锁已被当前线程锁持有且无法释放,这就导致线程在执行 method2() 时获取锁失败,会出现死锁问题。
- synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
synchronized 是依赖于 JVM 实现的,ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成)。