我试图在多线程上实现可重入锁,但由于某些原因,同一线程将解锁,然后再次锁定,从而导致始终使用同一线程来运行相同的操作。
下面是生成线程的代码
IntStream.range(0,(NUMBER_OF_THREADS)).forEach(index ->{
boolean operation = (index % 2 == 0) ? true : false;
Thread t = new Thread(new Client(operation,this));
t.start();
});
这是线程的运行功能的工作原理
@Override
public void run() {
while(!Thread.interrupted()) {
System.out.println("Trying to acquire lock : " + main.getLock().tryLock()
+ " thread id " + Thread.currentThread().getName());
// if (main.getLock().tryLock()) {
try {
main.getLock().lock();
if(main.getLock().isHeldByCurrentThread()) {
System.out.println("Lock held by this thread " + main.getLock().isHeldByCurrentThread()
+ " thread id : " + Thread.currentThread().getName());
if (operation) {
main.getcAaccount().deposit(1);
} else {
main.getcAaccount().withdraw(2);
}
Thread.currentThread().sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Thread id : " + Thread.currentThread().getName() + " unlocking");
main.getLock().unlock();//always have the unlock part here to ensure it unlock
}
}
它正确地打印出其他5个线程正试图获取该锁并失败,然后线程id ...正在解锁...并且即使该线程应该正在睡眠,该线程也立即再次锁定。
在这种逻辑情况下我错过了任何东西吗?
先谢谢您。
答案 0 :(得分:2)
重新进入要求每个锁定之后都有随后的解锁。例如,如果我调用lock.lock()
三次,则期望我也调用lock.unlock()
三次。在发生此事件序列之前,ReentrantLock
不会认为自己已解锁。
您没有意识到lock.tryLock()
,如果成功的话,本质上就像调用lock.lock()
。因此,通过lock
两次,您还需要两次unlock
。在您的代码示例中,您只能解锁一次,因此最初锁定的线程在技术上仍拥有该锁。
修复起来应该很简单,您可以从代码中删除第二个lock.lock()
,并且相互排斥仍然有效。要么,或者如果您需要锁定锁定,则将lock.tryLock()
替换为lock.lock()
。
根据您的编辑 ,您已解决了删除多余锁的问题,但现在您遇到了计时问题。您实际上并不需要tryLock
。您可以用lock
替换它,因为lock
的调用将挂起线程并阻塞(如果已经持有了锁(最终在调用解锁时唤醒))。