本章介绍介绍重入锁和不可重入锁的相关概念,以及使用简单的例子帮助理解什么是重入锁和不可重入锁。

可重入锁(ReentrantLock和synchronized)

可以重入锁的概念是一个线程可以对某个锁进行加锁,在不释放的情况下对该锁进行多次加锁,在释放的时候也需要进行多次释放,即成对出现。换句话说重入锁就是锁上加锁并且可以进行n次,伪代码如下:

//可重入锁的伪代码。
Lock lock =new Lock();
lock.lock();//加锁1
this.doSomeTask();//执行某些任务
lock.lock();//再加锁2
this.doSomeTask2();//执行任务2
lock.unLock();//解锁2
...
lock.unLock();//解锁1 

我们可以画一个简单示意图来理解它,如下:

Java中可重入锁

在Java中可以重入锁有我们之前介绍的 ReentrantLock和synchronized ,现在我们将之前的代码调整成重入锁的代码。

1)可重入锁ReentrantLock例子

public class ReentrantLockExample extends Thread {
    public static  Integer count=1;
    private static final Lock lock = new ReentrantLock();

    public ReentrantLockExample() { }

    @Override
    public void run() {
        while(true){
            if (count>10){
                System.out.println(getName() + "退出,count值: "+count);
                break;
            }

            try {
                lock.lock();                
                count = count +2;
                lock.lock();
                System.out.println(getName() + ",count值"+count);
                count = count - 1;

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                lock.unlock();
            }

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

    public static void main(String args[]) throws InterruptedException {
        ReentrantLockExample task1 = new ReentrantLockExample();
        ReentrantLockExample task2 = new ReentrantLockExample();

        task1.start();
        task2.start();

    }

} 

测试一下

在上面的例子中,我们使用了2次lock.lock()和2次lock.unlock()操作,先让count值加2,再让count值减1,最后count的输出结果也是顺序增长的。

Thread-0,count值2
Thread-1,count值3
Thread-0,count值4
Thread-1,count值5
Thread-1,count值6
Thread-0,count值7
Thread-1,count值8
Thread-0,count值9
Thread-1,count值10
Thread-0,count值11
Thread-1退出,count值: 11
Thread-0退出,count值: 11

 2)可重入锁Synchronized 例子

public class SynchronizedLockExample extends Thread {
    public static  Integer count=1;
    public static final String lock = "CountLock";


    @Override
    public void run() {
        while(true){
            synchronized(lock){
                if (count>20){
                    System.out.println(getName() + "退出,count值: "+count);
                    break;
                }
                count = count +2;
                //第二次加锁
                synchronized(lock){
                    count = count - 1;
                    System.out.println(getName() + ",count值"+count);
                }
            }
            //sleep放在synchronized外面
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String args[]) throws InterruptedException {
        SynchronizedLockExample task1 = new SynchronizedLockExample();
        SynchronizedLockExample task2 = new SynchronizedLockExample();

        task1.start();
        task2.start();

    }

}

测试一下

它和可重入锁ReentrantLock例子类似,我们使用了2次synchronized(lock)操作,先让count值加2,再让count值减1,最后count的输出结果也是顺序增长的。

但是Synchronized 的锁是自动释放的,而ReentrantLock需要手动释放。

了解了什么是可重入锁,我们再来介绍什么是不可重入锁。

不可重入锁(需自定义)

不可重入锁和可以重入锁的定义相反:

当我们的线程某个锁进行加锁,在不释放的情况下不会对后面的代码块进行阻塞。即不可重入锁就是加完锁之后需要释放,否则后续加锁的代码阻塞无效。换句话说不可重入锁只有第一次加锁的代码会阻塞。

我们可以画一个简单示意图来理解它,如下:

Java中不可重入锁

在Java中没有线程的类或者关键字可以实现不可重入锁,需要自己实现Java中的不可重入锁类或者关键字。

那么如何实现不可重入锁呢?我们借助Java中wait和notifyAll/notify 方法来实现锁的不可重入,当lock之后

下面使用ReentrantLock来实现不可重入锁。