java多线程中 锁 的概念的理解_java多线程之JUC包下ReentrantLock和Condition的理解

1.概念

Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能。 JDK的官方解释如下:条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。Condition实质上是被绑定到一个锁上。

2.核心

理解ReentrantLock以及condition的关键是要理解它的内部机制,其中核心就是:ReentrantLock的AQS队列、condition队列、以及condition.await()方法、condition.signal()方法。

我们需要弄清楚的是:

1.AQS队列和codition队列之间是什么关系???

2.condition.await()方法对condition队列以及AQS队列的影响??

3.condition.signal()方法对condition队列以及AQS队列的影响??

4.AQS队列和锁lock之间的关系??

3.用代码验证condition.await()方法public class TestAwaitMain {

private static ReentrantLock  lock;

public static void main(String[] args) {

// TODO Auto-generated method stub

//1.线程A先持有锁

//  然后await

//在B中查询是否能获取锁

//如果能则说明 await()方法可以释放锁

lock=new ReentrantLock();

Condition condition = lock.newCondition();

//开辟两个线程

new Thread(new RunnableA(lock,condition),"t_A").start();

try {

Thread.sleep(1000L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

new Thread(new RunnableB(lock,condition),"t_B").start();

lock.lock();

System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是  :"+lock.getWaitQueueLength(condition));

System.out.println("等待获取lock锁的"+"AQS队列长度是 :"+lock.getQueueLength());

}

}

5f29108d13bc?from=groupmessage

主测试程序

RunnableA的代码public class RunnableA implements Runnable {

private ReentrantLock lock;

private Condition condition;

public RunnableA(ReentrantLock lock, Condition condition) {

super();

this.lock = lock;

this.condition = condition;

}

@Override

public void run() {

System.out.println("线程A运行");

//先阻塞

lock.lock();

System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");

try {

System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());

System.out.println("持有锁的线程数:"+ lock.getHoldCount());

try {

System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");

System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的队列长度"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度"+lock.getWaitQueueLength(condition));

lock.hasWaiters(condition);

condition.await();

//此时,如果不执行await操作

//看看AQS队列的长度

Thread.sleep(10000L);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} finally {

// TODO: handle finally clause

//lock.unlock();

}

}

}

RunnableB的代码public class RunnableB implements Runnable {

private ReentrantLock lock;

private Condition condition;

public RunnableB(ReentrantLock lock, Condition condition) {

super();

this.lock = lock;

this.condition = condition;

}

@Override

public void run() {

System.out.println("线程B运行");

//先阻塞

lock.lock();

System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");

try {

System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());

System.out.println("持有锁的线程数:"+ lock.getHoldCount());

try {

System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");

System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的AQS队列长度是 :"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是  "+lock.getWaitQueueLength(condition));

//lock.hasWaiters(condition);

condition.await();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} finally {

// TODO: handle finally clause

lock.unlock();

}

}

}

运行结果

5f29108d13bc?from=groupmessage

运行结果

分析:

可见,在调用condition.await()方法时,其执行流程是这样的:1.释放当前线程持有的锁

2.把当前线程加入到condition队列的尾部,此时,condition的队列长度增加1。

3.当前线程会在await()方法中不断尝试自旋获取锁。如果获取不到锁,那么线程A就无法从await()方法中返回。

4.用代码验证condition.signal()方法

/**

*

* 验证signal的作用:

*

*    1.线程A先lock

*    2.线程A再await

*    3.线程B执行lock

*    4.线程B执行signal操作

*    5.此时,检验线程Bsignal之后,线程A的await后面的代码能执行吗???

*

*    //如果线程B只signal而不释放锁的话,此时线程A能从await中醒来吗????

* @author chihaojie

*

*/

public class TestSignalMain {

private static ReentrantLock  lock;

public static void main(String[] args) {

// TODO Auto-generated method stub

//1.线程A先持有锁

//  然后await

//在B中查询是否能获取锁

//如果能则说明 await()方法可以释放锁

lock=new ReentrantLock();

Condition condition = lock.newCondition();

//开辟两个线程

new Thread(new RunnableA(lock,condition),"t_A").start();

try {

Thread.sleep(1000L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

new Thread(new RunnableB(lock,condition),"t_B").start();

}

}

5f29108d13bc?from=groupmessage

主测试程序

RunnableA的代码

public class RunnableA implements Runnable {

private ReentrantLock lock;

private Condition condition;

public RunnableA(ReentrantLock lock, Condition condition) {

super();

this.lock = lock;

this.condition = condition;

}

@Override

public void run() {

System.out.println("线程A运行");

//先阻塞

lock.lock();

System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");

try {

System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());

System.out.println("持有锁的线程数:"+ lock.getHoldCount());

try {

System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");

System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的队列长度"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"的Condition队列长度"+lock.getWaitQueueLength(condition));

condition.await();

System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后,持有锁吗 ?:"+lock.isHeldByCurrentThread());

//signal之后:

//condition队列长度为0,AQS队列长度为1

System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后:"+"AQS队列长度是 :"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后:"+"Condition队列长度是 :"+lock.getWaitQueueLength(condition));

//此时,如果不执行await操作

//看看AQS队列的长度

//醒来之前的必须先获取锁

//That thread must then re-acquire the lock before returning from await.

Thread.sleep(10000L);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} finally {

// TODO: handle finally clause

lock.unlock();

}

}

}

RunnableB的代码public class RunnableB implements Runnable {

private ReentrantLock lock;

private Condition condition;

public RunnableB(ReentrantLock lock, Condition condition) {

super();

this.lock = lock;

this.condition = condition;

}

@Override

public void run() {

System.out.println("线程B运行");

//先阻塞

lock.lock();

System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");

try {

System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());

try {

System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的AQS队列长度是 :"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是  "+lock.getWaitQueueLength(condition));

//lock.hasWaiters(condition);

condition.signal();

//线程B再执行了signal()方法之后,我们看一下此时:AQS队列的情况以及condition队列的情况

System.out.println("线程"+Thread.currentThread().getName()+"执行signal()方法之后等待获取锁的AQS队列长度是 :"+lock.getQueueLength());

System.out.println("线程"+Thread.currentThread().getName()+"执行signal()方法之后Condition队列长度是  "+lock.getWaitQueueLength(condition));

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} finally {

// TODO: handle finally clause

//V2:注释掉lock.unlock();

//即: 如果线程B不释放锁的话,即使线程B调用了signal()方法,那么线程A能从await方法返回吗???

//V3

lock.unlock();

}

}

}

运行结果:

5f29108d13bc?from=groupmessage

运行结果

分析结果:

从上面的运行结果,我们可以得知:

signal()方法都做了哪些事情???

signal()方法:1.将condition队列的头节点,从condition队列中移除

2.把上面移除的节点加入到AQS队列的尾部

3.让其等待再次获取锁。

可见,当线程B执行了signal()之后,AQS队列的长度增加,condition队列的长度减少。并且线程A会一直在await()方法中尝试自旋获取锁

如果获取不到锁,那么线程A就无法从await()方法中返回。

5.附上一张原理图,方便大家理解

5f29108d13bc?from=groupmessage

await()方法导致condition队列变长

5f29108d13bc?from=groupmessage

signal()方法让Codition队列减短,同时让AQS队列增长

6.补充

对于lock.lock()方法的理解:

我们先来看一下,下面的这段代码:try {

lock.lock();

if (storage > 0) {

putCondition.await();

}

storage++;

System.out.println("put => " + ++putCounter );

getCondition.signal();

} finally {

lock.unlock();

}

我们要知道

ReentrantLock是一个独占式的锁lock.lcck();1.lock.lock()这是获取锁的操作

2.但是这把锁有可能已经被占用了

3.如果获取锁成功,即线程继续往下执行

4.如果锁已经被其他线程获取了,

5.则该线程会被阻塞住,然后加入到AQS队列中。

即: lock.lock()方法如果获取不到锁的话,就会把当前线程加入到AQS队列的尾部。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值