synchronized 和 ReentrantLock的区别
在讨论synchronized 和 ReentrantLock的区别前,我们先了解一下什么是公平锁和非公平锁
一、 公平锁和非公平锁
从公平的角度来说,Java 中的锁总共可分为两类:公平锁和非公平锁。但公平锁和非公平锁有哪些区别?孰优孰劣呢?在 Java 中的应用场景又有哪些呢?接下来我们一起来看。
1. 定义
公平锁:每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的,最前面的线程总是最先获取到锁。
非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,所有线程会竞争获取锁。
2. 代码演示
接下来我们使用 ReentrantLock 来演示一下公平锁和非公平锁的执行差异,首先定义一个公平锁,开启 3 个线程,每个线程执行两次加锁和释放锁并打印线程名的操作,
如下代码所示:
非公平锁:
1 public class ReentrantLockFairTest { 2 static Lock lock = new ReentrantLock(); 3 public static void main(String[] args) throws InterruptedException { 4 for (int i = 0; i < 3; i++) { 5 new Thread(() -> { 6 for (int j = 0; j < 2; j++) { 7 lock.lock(); 8 try { 9 System.out.println("当前线程:" + Thread.currentThread().getName()); 10 } finally { 11 lock.unlock(); 12 } 13 } 14 }).start(); 15 } 16 } 17 } 18 19 //执行结果 20 当前线程:Thread-0 21 当前线程:Thread-0 22 当前线程:Thread-1 23 当前线程:Thread-1 24 当前线程:Thread-2 25 当前线程:Thread-2
公平锁:
1 public class ReentrantLockUnfairTest { 2 static Lock lock = new ReentrantLock(true); 3 public static void main(String[] args) throws InterruptedException { 4 for (int i = 0; i < 3; i++) { 5 new Thread(() -> { 6 for (int j = 0; j < 2; j++) { 7 lock.lock(); 8 try { 9 System.out.println("当前线程:" + Thread.currentThread().getName()); 10 } finally { 11 lock.unlock(); 12 } 13 } 14 }).start(); 15 } 16 } 17 } 18 19 //执行结果 20 当前线程:Thread-0 21 当前线程:Thread-1 22 当前线程:Thread-2 23 当前线程:Thread-0 24 当前线程:Thread-1 25 当前线程:Thread-2
说明:
从上述结果可以看出,使用公平锁线程获取锁的顺序是:A -> B -> C -> A -> B -> C,也就是按顺序获取锁。而非公平锁,获取锁的顺序是 A -> A -> B -> B -> C -> C,原因是所有线程都争抢锁时,因为当前执行线程处于活跃状态,其他线程属于等待状态(还需要被唤醒),所以当前线程总是会先获取到锁,所以最终获取锁的顺序是:A -> A -> B -> B -> C -> C。
3. 公平锁和非公平锁有何区别
公平性是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时间最久的线程。公平性是减少线程“饥饿”(个别线程长期等待锁,但始终无法获取)情况发生的一个办法。
1)公平锁能保证:老的线程排队使用锁,新线程仍然排队使用锁。
2)非公平锁保证:老的线程排队使用锁;但是无法保证新线程抢占已经在排队的线程的锁。
看下面代码案例所示:可以得出结论,公平锁指的是哪个线程先运行,那就可以先得到锁。非公平锁是不管线程是否是先运行,新的线程都有可能抢占已经在排队的线程的锁。
4. 优缺点分析
公平锁的优点是按序平均分配锁资源,不会出现线程饿死的情况,它的缺点是按序唤醒线程的开销大,执行性能不高。
非公平锁的优点是执行效率高,谁先获取到锁,锁就属于谁,不会“按资排辈”以及顺序唤醒,但缺点是资源分配随机性强,可能会出现线程饿死的情况。
二、synchronized
三、ReentrantLock
四、synchronized 和 ReentrantLock的区别
五、总结
在 Java 语言中,锁的默认实现都是非公平锁,原因是非公平锁的效率更高,使用 ReentrantLock 可以手动指定其为公平锁。非公平锁注重的是性能,而公平锁注重的是锁资源的平均分配,所以我们要选择合适的场景来应用二者。
在 Java 语言中,锁 synchronized 和 ReentrantLock 默认都是非公平锁,当然我们在创建 ReentrantLock 时,可以手动指定其为公平锁,但 synchronized 只能为非公平锁。
参考链接:
https://www.cnblogs.com/vipstone/p/16248006.html
https://juejin.cn/post/6844903695298068487
标签:synchronized,Thread,区别,lock,ReentrantLock,线程,公平 From: https://www.cnblogs.com/hld123/p/18263975