源码分析
接下来我们从源码角度来看看 ReentrantLock 的实现原理, 它是如何保证可重入性, 又是如何实现公平锁的.
1, 无参构造器 (默认为非公平锁)
- public ReentrantLock() {
- sync = new NonfairSync();// 默认是非公平的
- }
sync 是 ReentrantLock 内部实现的一个同步组件, 它是 Reentrantlock 的一个静态内部类, 继承于 AQS.
2, 带布尔值的构造器 (是否公平)
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();//fair 为 true, 公平锁; 反之, 非公平锁
- }
此处可以指定是否采用公平锁, FailSync 和 NonFailSync 亦为 Reentrantlock 的静态内部类, 都继承于 Sync.
- 3,lock()
- public void lock() {
- sync.lock();// 代理到 Sync 的 lock 方法上
- }
Sync 的 lock 方法是抽象的, 实际的 lock 会代理到 FairSync 或是 NonFairSync 上 (根据用户的选择来决定, 公平锁还是非公平锁)
- 4,unlock()
- public void unlock() {
- sync.release(1);// 释放锁
- }
释放锁, 调用 sync 的 release 方法.
- 5,tryLock()
- Lock lock = ...;
- if(lock.tryLock()) {
- try{
- // 处理任务
- }catch(Exception ex){
- }finally{
- lock.unlock(); // 释放锁
- }
- }else {
- // 如果不能获取锁, 则直接做其他事情
- }
tryLock() 方法是有返回值的, 它表示用来尝试获取锁, 如果获取成功, 则返回 true, 如果获取失败 (即锁已被其他线程获取), 则返回 false.
- 6,newCondition()
- public Condition newCondition() {
- return sync.newCondition();
- }
获取一个 conditon,ReentrantLock 支持多个 Condition
- 7,await()
- public class MyService {
- private Lock lock = new ReentrantLock();
- private Condition condition=lock.newCondition();
- public void testMethod() {
- try {
- lock.lock();
- System.out.println("开始 wait");
- condition.await();
- for (int i = 0; i < 5; i++) {
- System.out.println("ThreadName=" + Thread.currentThread().getName()
- + (" " + (i + 1)));
- }
- } catch (InterruptedException e) {
- // TODO 自动生成的 catch 块
- e.printStackTrace();
- }
- finally
- {
- lock.unlock();
- }
- }
- }
通过创建 Condition 对象来使线程 wait, 必须先执行 lock.lock 方法获得锁
- 8,signal()
- public void signal() {
- try {
- lock.lock();
- condition.signal();
- } finally {
- lock.unlock();
- }
- }
condition 对象的 signal 方法可以唤醒 wait 线程
9, 创建多个 condition 对象
一个 condition 对象的 signal(signalAll) 方法和该对象的 await 方法是一一对应的, 也就是一个 condition 对象的 signal(signalAll) 方法不能唤醒其他 condition 对象的 await 方法
ABC 循环打印 20 遍
- package main.java.Juc;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- /*
- * 编写一个程序, 开启 3 个线程, 这三个线程的 ID 分别为 A,B,C, 每个线程将自己的 ID 在屏幕上打印 10 遍, 要求输出的结果必须按顺序显示.
- * 如: ABCABCABC...... 依次递归
- */
- public class TestABCAlternate {
- public static void main(String[] args) {
- AlternateDemo ad = new AlternateDemo();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 20; i++) {
- ad.loopA(i);
- }
- }
- }, "A").start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 20; i++) {
- ad.loopB(i);
- }
- }
- }, "B").start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 20; i++) {
- ad.loopC(i);
- System.out.println("-----------------------------------");
- }
- }
- }, "C").start();
- }
- }
- class AlternateDemo{
- private int number = 1; // 当前正在执行线程的标记
- private Lock lock = new ReentrantLock();
- private Condition condition1 = lock.newCondition();
- private Condition condition2 = lock.newCondition();
- private Condition condition3 = lock.newCondition();
- /**
- * @param totalLoop : 循环第几轮
- */
- public void loopA(int totalLoop){
- lock.lock();
- try {
- //1. 判断
- if(number != 1){
- condition1.await();
- }
- //2. 打印
- for (int i = 1; i <= 1; i++) {
- System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
- }
- //3. 唤醒
- number = 2;
- condition2.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- public void loopB(int totalLoop){
- lock.lock();
- try {
- //1. 判断
- if(number != 2){
- condition2.await();
- }
- //2. 打印
- for (int i = 1; i <= 1; i++) {
- System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
- }
- //3. 唤醒
- number = 3;
- condition3.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- public void loopC(int totalLoop){
- lock.lock();
- try {
- //1. 判断
- if(number != 3){
- condition3.await();
- }
- //2. 打印
- for (int i = 1; i <= 1; i++) {
- System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
- }
- //3. 唤醒
- number = 1;
- condition1.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
运行结果:
代码分析:
三个线程分别循环 20 次调用 loopA,loopB,loopC 打印, 但是不确定是哪个方法先被调用到, 如果是 loopB 先调用, 则 loopB 方法先获取到锁, loopA 和 loopC 等待锁, 此时线程执行标记 number=1, 代码 84 行处为 true, 则 condition2.await(); 如果需要唤醒此线程, 则需要用 condition2 来唤醒, 此时线程交出锁;
如果 loopA 获取了锁, loopB 和 loopC 等待锁, 此时线程执行标记 number=1, 代码 63 行处为 false, 则执行 67 行打印, 打印完则用 condition2.signal() 唤醒打印 loopB 的线程, 接着 loopB 的线程去打印 B, 线程 loopB 打印完毕去唤醒打印 loopC 的线程, 打印完 loopC 再唤醒 loopA, 如此循环 20 次.
总结
1,Lock 是一个接口, 而 synchronized 是 Java 中的关键字, synchronized 是内置的语言实现;
2,synchronized 在发生异常时, 会自动释放线程占有的锁, 因此不会导致死锁现象发生; 而 Lock 在发生异常时, 如果没有主动通过 unLock() 去释放锁, 则很可能造成死锁现象, 因此使用 Lock 时需要在 finally 块中释放锁;
3,Lock 类可以创建 Condition 对象, Condition 对象用来是线程等待和唤醒线程, 需要注意的是 Condition 对象的唤醒的是用同一个 Condition 执行 await 方法的线程, 所以也就可以实现唤醒指定类的线程
来源: https://www.cnblogs.com/java-chen-hao/p/10037209.html