本章介绍介绍重入锁和不可重入锁的相关概念,以及使用简单的例子帮助理解什么是重入锁和不可重入锁。
可重入锁(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中可以重入锁有我们之前介绍的 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
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中wait和notifyAll/notify 方法来实现锁的不可重入,当lock之后
下面使用ReentrantLock来实现不可重入锁。