一篇文章让你彻底了解Java可重入锁和不可重入锁

网友投稿 244 2023-01-11

一篇文章让你彻底了解Java可重入锁和不可重入锁

可重入锁

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。

我的理解就是,某个线程已经获得某个锁,可以无需等待而再次获取锁,并且不会出现死锁(不同线程当然不能多次获得锁,需要等待)。

简单的说,就是某个线程获得某个锁,之后可以不用等待而再次获取锁且不会出现死锁。

常见的可重入锁

Synchronized和ReentrantLock 都是可重入锁。

可重入锁的释放

同一个线程获取同一个锁,状态值state会累加,假设state累加到了2,每释放一次锁会减1,只有当状态值state减到0了,其他线程才有机会获取锁。也就是说,state归零才是已释放锁的标致。

可重入锁示例

public class ReentrantTest implements Runnable {

@Override

public void run() {

get();

}

public synchronized void get() {

System.out.println(Thread.currentThread().getName());

set();

}

/**

* 递归方法

*/

public synchronized void set() {

System.out.println(Thread.currentThread().getName());

}

/**

* 这里的对象锁只有一个,就是rt对象的锁。

* 当执行rt的get方法时,该线程获得rt对象的锁。在get方法内执行set方法时再次请求rt对象的锁,因为synchronized是可重入锁,所以又可以得到该锁。循环这个过程。

* 假设不是可重入锁的话,那么请求的过程中会出现阻塞,从而导致死锁。

* @param args

*/

public static void main(String[] args) {

ReentrantTest rt = new ReentrantThttp://est();

// for(;;)模拟无限循环

for(;;){

new Thread(rt).start();

}

}

}

分析:这里的对象锁只有一个,就是rt对象的锁。当执行rt的get方法时,该线程获得rt对象的锁。在get方法内执行set方法时再次请求rt对象的锁,因为synchronized是可重入锁,所以又可以得到该锁。循环这个过程。假设不是可重入锁的话,那么请求的过程中会出现阻塞,从而导致死锁。

死锁

多线程中,不同的线程都在等待其它线程释放锁,而其它线程由于一些原因迟迟没有释放锁。程序的运行处于阻塞状态,不能正常运行也不能正常终止。

运行结果

set()和get()同时输出了相同的线程名称,也就是说某个线程执行的时候,不仅进入了set同步方法,还进入了get同步方法。递归使用synchronized也没有发生死锁,证明其是可重入的。

可重入锁的实现原理?

每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增1;当线程退出同步代码块时,计数器会递减1,如果计数器为 0,则释放该锁。

再分析一下上面可重入锁的例子

递归调用一次同步代码块,计数器会变为2,整个递归调用执行完,先退出内层执行减1,再退出外层执行减1。然后释放锁。

不可重入锁

就是某个线程已经获得某个锁,之后不可以再次获取锁,会被阻塞。

设计一个不可重入锁

public class Lock {

private boohAofDlean isLocked = false;

/**

* 加锁

*/

public synchronized void lock() throws Exception{

while(isLocked){

//当前线程释放锁,让出CPU,进入等待状态,直到被唤醒,才继续执行15行

wait();

System.out.println("wait");

}

isLocked = true;

}

/**

* 解锁

*/

public synchronized void unlock(){

isLocked = false;

//唤醒一个等待的线程继续执行

notify();

}

}

测试

public class Test {

Lock lock = new Lock();

public void print() throws Exception{

//加锁 标记为true

lock.lock();

//释放锁->等待 阻塞在16行

doAdd();

lock.unlock();

}

public void doAdd() throws Exception{

lock.lock();

System.out.println("doAdd");

lock.unlock();

}

public static void main(String[] args)throws Exception {

Test test=new Test();

test.print();

}

}

结hAofD果:这里,虽然模拟的是不可重入锁,实际还是在单线程环境中的。当前线程执行print()方法首先加锁 标记为true,接下来释放锁->等待 阻塞在16行内部的14行。整个过程中,第一次进入lock同步方法,执行完毕,第二次进入lock同步方法,阻塞等待。这个例子很好的说明了不可重入锁。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:最新IntelliJ IDEA 2021版配置 Tomcat 8.5 的详细步骤
下一篇:杭州顺丰快递物流查询单号(顺丰快递递运单号查询)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~