【Java深入学习】ReentrantLock的深入讲解-下

69 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

lockInterruptibly方法

代码示例

public class TestInterReen {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            try {
                System.out.println("t1尝试获取锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("t1被打断");
                return;
            }

            try {
                System.out.println("t1获取到锁");
            }finally {
                lock.unlock();
            }
        });

        Thread t2 = new Thread(()->{
            try {
                System.out.println("t2尝试获取锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("t2被打断");
                return;
            }
            try {
                System.out.println("t2获取到锁");
                Sleeper.sleep(2);
            }finally {
            }
        });
        t2.start();
        Sleeper.sleep(0.5);
        t1.start();
        Sleeper.sleep(0.5);
        System.out.println("打断正在等待锁的t1线程");
        t1.interrupt();
    }

}

结果:

程序结束:
t2尝试获取锁
t2获取到锁
t1尝试获取锁
打断正在等待锁的t1线程
t1被打断

解释:

可以看出我们用lockInterruptibly方法可以实现 打断处于阻塞状态的线程

锁超时

锁超时是指,一旦锁被其他线程占用 如果没有指定超时时间 则本线程会直接跳出阻塞状态 不参与竞争,如果指定了超时时间 则 超时时间内处于阻塞状态 超出超时时间 则跳出阻塞状态。

代码示例

@Slf4j(topic = "c.TestInterCS")
public class TestInterCS {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            log.debug("t1尝试获取锁");
            if (!lock.tryLock()){
                    log.debug("t1获取不到锁");
                    return;
            }

            try {
                log.debug("t1获取到锁");
            }finally {
            }
        },"t1");

        Thread t2 = new Thread(()->{
            log.debug("t2尝试获取锁");
            if (!lock.tryLock()){
                log.debug("t2获取不到锁");
                return;
            }

            try {
                log.debug("获取到锁");
            }finally {
                lock.unlock();
            }
        },"t2");
        t1.start();
        Sleeper.sleep(1);
        t2.start();
    }
}

结果:

  • 00:40:27.595 c.TestInterCS [t1] - t1尝试获取锁
  • 00:40:27.597 c.TestInterCS [t1] - t1获取到锁
  • 00:40:28.600 c.TestInterCS [t2] - t2尝试获取锁
  • 00:40:28.600 c.TestInterCS [t2] - t2获取不到锁

解释:

可以看出t1线程获取到锁并且没有释放,t2线程一直处于阻塞状态,我们用tryLock方法 且没有指定超时时间 也就是一旦发现t2线程处于阻塞状态lock.tryLock()返回false,我们执行其它代码

代码示例

@Slf4j(topic = "c.TestInterCS")
public class TestInterCS {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            log.debug("t1尝试获取锁");
            try {
                if (!lock.tryLock(2, TimeUnit.SECONDS)){
                    log.debug("t1获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                log.debug("t1获取到锁");
            }finally {
            }
        },"t1");

        Thread t2 = new Thread(()->{
            log.debug("t2尝试获取锁");
            if (!lock.tryLock()){
                log.debug("t2获取不到锁");
                return;
            }

            try {
                log.debug("获取到锁");
            }finally {
                lock.unlock();
            }
        },"t2");
        t1.start();
        Sleeper.sleep(1);
        t2.start();
    }
}

结果:

  • 00:44:46.180 c.TestInterCS [t1] - t1尝试获取锁
  • 00:44:46.183 c.TestInterCS [t1] - t1获取到锁
  • 00:44:47.183 c.TestInterCS [t2] - t2尝试获取锁
  • 00:44:47.183 c.TestInterCS [t2] - t2获取不到锁

解释:

我们给tryLock方法指定时间为2s,但是因为t1线程始终没有释放锁,所以结果lock.tryLock()依然返回false,我们进行逻辑判断,执行其它的代码

条件变量

基本介绍

synchronized中也有条件变量,就是我们讲原理时Monitor中的WaitSet,当条件不满足时进入WaitSet中等待。 ReentrantLock的条件变量比synchronized强大之处在于 它是之处多个条件变量的 这就好比 synchronized是那些不满足条件的线程都在一间休息室等消息,而ReentrantLock支持多间休息室 有专门等烟条件的休息室 有专门等待早餐条件的休息室 唤醒时也是按照休息室来唤醒

使用流程

1.await前需要获取锁
2.await执行后 会释放锁 进入conditionObject等待
3.await的线程被唤醒(或打断 或超时)去重新竞争lock锁
4.竞争lock锁成功后 从await后继续执行

代码示例

@Slf4j(topic = "c.Test24")
public class Test24 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;
    static ReentrantLock ROOM = new ReentrantLock();
    // 等待烟的休息室
    static Condition waitCigaretteSet = ROOM.newCondition();
    // 等外卖的休息室
    static Condition waitTakeoutSet = ROOM.newCondition();

    public static void main(String[] args) {


        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("有烟没?[{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        waitCigaretteSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        waitTakeoutSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小女").start();

        sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeout = true;
                waitTakeoutSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送外卖的").start();

        sleep(1);

        new Thread(() -> {
            ROOM.lock();
            try {
                hasCigarette = true;
                waitCigaretteSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送烟的").start();
    }

}

结果:

01:20:49.292 c.Test24 [小南] - 有烟没?[false] 01:20:49.295 c.Test24 [小南] - 没烟,先歇会! 01:20:49.295 c.Test24 [小女] - 外卖送到没?[false] 01:20:49.295 c.Test24 [小女] - 没外卖,先歇会! 01:20:50.300 c.Test24 [小女] - 可以开始干活了 01:20:51.309 c.Test24 [小南] - 可以开始干活了

解释:

  • 01:20:49.292 c.Test24 [小南] - 有烟没?[false]
  • 01:20:49.295 c.Test24 [小南] - 没烟,先歇会!
  • 01:20:49.295 c.Test24 [小女] - 外卖送到没?[false]
  • 01:20:49.295 c.Test24 [小女] - 没外卖,先歇会!
  • 01:20:50.300 c.Test24 [小女] - 可以开始干活了
  • 01:20:51.309 c.Test24 [小南] - 可以开始干活了