【ReentrantLock】源码深入浅出

94 阅读3分钟

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

基本介绍

RennttrantLock也是锁和synchronized一样具有锁定同步代码块的作用,不过和synchronized还是有很多不一样的地方。 RennttrantLock是java.util.concurrent(juc) 下的一个类,相对于synchronized它具备如下特点:

  • 可以打断处于BLOCKED状态的其他线程
  • 可以设置处于BLOCKED状态的线程超时时间,如果超过这个时间就放弃争抢锁
  • 可以设置为公平锁,线程按照先入先出获取锁
  • 支持多个条件变量,这个是指 对于等待区 我们可以由多个 不同对象调用锁处于等待状态时所在的等待区不同,对于synchronized锁来说 它的等待区就只有一个WaitSet

与synchronized一样 都支持锁重入,即自己可以再次获取自己内部的锁

基本语法

	ReentrantLock lock = new ReentrantLock();
	lock.lock();
	try{
		// lock.lock();
		// 临界区
	}finally{
		// 释放锁
		lock.unlock();
	}

注意:lock.lock();可以放在try外面,也可以放在try里面,最后在finally中需要进行lock.unlock();释放锁

可锁重入

代码示例

public class TestInterKCR {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock();
        try {
            System.out.println("enter main");
            m1();
        }finally {
            lock.unlock();
        }
    }
    private static void m1(){
        lock.lock();
        try {
            System.out.println("enter m1");
            m2();
        }finally {
            lock.unlock();
        }
    }

    private static void m2(){
        lock.lock();
        try {
            System.out.println("enter m2");
        }finally {
            lock.unlock();
        }
    }
}

结果:

enter main
enter m1
enter m2

解释:

可以看出自己内部的锁都可以调用,没有发生死锁,说明可以锁重入

可打断

我们先要明确一个概念,这里说的可打断 说的是 一个已经获得锁的线程t1 和一个处于阻塞状态线程的t2(因为t1已经获取了锁),我们可以在其他线程中 把 t2线程打断 使之 跳出阻塞状态 运行其它部分代码。

那么为什么 synchronized不可以被打断?

代码示例

public class TestInterSync {
    private static String a = "s";
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            System.out.println("t1尝试获取锁");
            synchronized (a){
                System.out.println("t1获取到锁");
                    boolean interrupted = Thread.currentThread().isInterrupted();
                    if (interrupted){
                        System.out.println("t1被打断");
                    }
            }
        });

        Thread t2 = new Thread(()->{
            System.out.println("t2尝试获取锁");
            synchronized (a){
                System.out.println("t2获取到锁");
                while (true){

                }
            }
        });
        t2.start();
        Sleeper.sleep(0.5);
        t1.start();
        Sleeper.sleep(0.5);
        System.out.println("打断正在等待锁的t1线程");
        t1.interrupt();
    }

}

结果:

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

解释:

我们发现程序并没有结束,我们的t2线程先获取到锁且没有释放锁,导致t1线程阻塞,现在我们在主线程中打断t1线程 但是发现无法打断

lOC可以被打断吗?

代码示例

public class TestInterReenY {
        private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{

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

        Thread t2 = new Thread(()->{
            System.out.println("t2尝试获取锁");
            lock.lock();
            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线程

解释:

我们发现lock方法依然不能打断处于阻塞状态的线程,那我们应该用什么,答案是lockInterruptibly方法