为什么有多个线程获取到锁,不是应该只有一个可以获取锁么?其他的要等第一个释放才能获取
来源:6-5 J.U.C之AQS-ReentrantLock与锁-1
![](http://img1.sycdn.imooc.com/user/533e4c3300019caf02000200-100-100.jpg)
慕粉3349069
2018-06-03
运行结果如下:
测试代码如下:请老师指导。
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 100;
public static int count = 0;
private final static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
add();
} catch (Exception e) {
log.error("exception", e);
}
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
lock.lock();
try {
count++;
System.out.println(count + "=====" + lock.getHoldCount());
} finally {
// lock.unlock();
}
}
=================================================
(谢谢老师的回答,学生现在学习和测试该功能)还烦老师再次指导。
第二次提问:
前提是add方法没有unlock释放锁。
有以下3种情况:
1、在线程池中加入sleep
执行结果一直保持1===========1。
2、在for循环中加入sleep
会继续执行一段时间,但是不会执行到5000。
3、直接执行add()
对于可重入锁的理解如下,
1、如果该锁不被另一个线程持有并立即返回,则将该锁设置为一个。(该点容易理解)
2、如果当前线程已经保存了锁,则保持计数递增1,并且方法立即返回。(针对上面3种情况的测试出现不同的结果,还烦请老师指导学生一下)
3、如果锁由另一线程持有,那么当前线程在线程执行调度时被禁用并且处于休眠状态,直到锁已被获取,此时锁保持计数被设置为一。(该点学生理解的是:该锁只能被一个线程调用(或者重复调用),第二个线程需要等待第一个锁释放才能获取;但是学生的疑问是,学生的第一次提问,执行结果为什么是有多个线程都获取到锁了?)
2回答
-
第一,不要通过修改题目来提其他问题,容易被我忽略,因为我点过去会发现我已经回复过。回过头看讨论过程也会怪怪的,变成我一直在补充回答。
第二,不要使用system.out的打印结果来验证多线程的输出结果,请使用课程里介绍的log或者自己定义一个log。
你这样输出的结果是无法具体确认是哪个线程执行的,尤其是你要分析的内容与哪个线程执行有关,而log在输出日志则会带上时间和实际执行的线程,这两项对结果的分析至关重要。
线程池在实际执行代码时,尤其是add这种简单操作时,很可能只拿出一个线程去执行就可以了。你问题里说的不同线程,你的根据是什么?
第三,说说你第2个case和第3个case的想法,我没看出想表达啥,尤其是第2个
第四,说一下遇到不懂的问题自己该怎么办。第一次运行时发现阻塞,你在肯定会阻塞相关的代码里加上断点,看看当时一些细节的数据,答案可能直接就出来了。
最后,多线程环境下输出的日志一定要带上线程名和执行时间,这些对分析至关重要012018-06-04 -
Jimin
2018-06-03
你好,这是因为ReentrantLock是可重入锁,当一个线程得到一个对象后,再次请求该对象锁时是可以再次得到该对象的锁的。具体就是自己可以再次获取自己的内部锁。
你可以打开ReentrantLock的lock方法的源码注释:
/**
* Acquires the lock.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
* at which time the lock hold count is set to one.
*/
public void lock() {
sync.lock();
}这里写的还是很清楚,如果你想完全保证第一次执行完第二次再执行,那就使用synchronized修饰这个方法吧。
目前你这种写法,由于没有unlock,会导致资源一直得不到释放,方法一直无法结束,最后出现OutOfMemory异常。而且你这里调用的getHoldCount()方法通常一般只在测试时用。
关于ReentrantLock锁的一些说明及,也可以参考一些博客说明,比如:
https://www.jianshu.com/p/96c89e6e7e90
012018-06-04
相似问题