首先我们要明白锁的概念,在多线程的场景下,不同的线程一起运行会导致脏数据,为了保证线程安全,这时候便出现了锁的概念,锁可以对代码块,资源,对象上锁,上锁之后,只允许一个线程对其进行操作,其他线程都会进入阻塞状态,这也就是阻塞加锁方法,直到锁释放之后,其他线程才可以获取锁对其操作。
之前文章重点单独介绍过Synchronized和Reentrantlock,不明白的同学可以先看看了解:https://blog.youkuaiyun.com/ke1ying/article/details/117419257?spm=1001.2014.3001.5501
这篇文章主要是介绍可重入锁和不可重入锁,在jdk中synchronized和Reentrantlock,都是可重入锁,为了更高效的性能和防止发生死锁。
可重入锁可以理解为:同一个线程下,外层方法上锁之后,内层调用的方法也能正常获取锁。
下面先用代码介绍对【不可重入锁】的理解。
Lock lock = new ReentrantLock();
public void testA(){
try{
//获取锁之后才能进入业务
lock.lock();
//业务处理...
testB();
}catch (Exception e){
}finally {
lock.unlock();
}
}
private void testB() {
try{
//获取锁之后才能进入业务
lock.lock();
//业务处理...
}catch (Exception e){
}finally {
lock.unlock();
}
}
当testA()方法获取锁之后,在业务里调用testB(),此时因为A的锁未释放,所以B不能获取到锁,这时候B的业务不能正常进行下去,导致A也不能正常释放锁,A和B方法就发生了死锁。这就是不可重入锁。
可重入锁是什么呢?
Jdk中带的基本都是可重入锁,下面就用synchronized实例介绍,在锁住同一个object之后,控制台打印可以看出,threadA调用threadB方法,B和A方法不会发生死锁,业务都能进行下去。
打印为:
@Log4j2
public class SynchronizedController {
public static void main(String[] args) {
new Thread(new ThreadTest()).start();
}
static class ThreadTest implements Runnable {
private final Object obj = new Object();
private void threadA() {
synchronized (obj) {
log.info("threadA正常运行...");
threadB();
}
}
private void threadB() {
synchronized (obj) {
log.info("threadB正常运行...");
}
}
@Override
public void run() {
threadA();
}
}
}
我们自己可以用synchronized + wait + notify来实现不可重入锁,代码如下:
/**
* 用synchronized 和 wait 和 notify来实现不可重入锁
*/
private boolean flag = false;
public synchronized void lock() {
while (flag) {
//当有其他线程获取锁,则进入wait状态
try {
wait();
} catch (Exception e) {
}
}
flag = true;
}
public synchronized void unlock() {
//释放锁,并且随机唤醒一个wait的线程
flag = false;
notify();
}