今天在回顾java并发编程时遇到一个问题,有点奇怪
代码如下
class BlockMutex {
private Lock lock = new ReentrantLock();
public BlockMutex() {
lock.lock();
}
public void f(String name) {
lock.lock();
System.out.println(name+" lock acquired in f() ");
}
}
BlockMutex是一个可能存在并发问题的资源,所以我使用lock以实现同步,创建该对象的线程在创建的时候就会持有对象锁,如构造器,在执行f()方法时候也要先申请锁
class BlockThread implements Runnable {
//线程创建BlockThread同时就持有了blockMutex锁
private BlockMutex blockMutex = new BlockMutex();
public BlockMutex getBlockMutex() {
return blockMutex;
}
@Override
public void run() {
System.out.println("waiting lock in f()");
blockMutex.f("BlockThread");
System.out.println("broken out of blocked call");
}
}
这是一个Task task初始化的时候,该task就会持有锁,当task执行run方法的时候,会执行f()函数,因为BlockThread会初始化BlockMutex时就持有了BlockMutex的对象锁,而ReentrantLock是支持重入的,所以BlockThread理所应当继续执行f()方法
public static void main(String[] args) throws InterruptedException {
BlockThread blockThread=new BlockThread();
Thread thread = new Thread(blockThread);
thread.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("main has finished ");
}
在main函数中进行测试
运行结果如下
waiting lock in f()
main has finished
我感觉奇怪的是blockThread线程在执行时竟然一直阻塞在f()方法中,按理说BlockThread已经持有了BlockMutex的对象锁,不应该继续申请锁.经过一段时间思考后我明白了blockThread线程并没有持有BlockMutex的对象锁,blockThread初始化是在主线程中进行,而锁的申请是在构造器中完成,所以持有BlockMutex的对象锁是主线程,为了验证这一思想,我们只要让主线程执行f()方法,如果无等待执行成功则我们这一思想成立,更改main函数如下
public class LockInterruptTest {
public static void main(String[] args) throws InterruptedException {
BlockThread blockThread=new BlockThread();
Thread thread = new Thread(blockThread);
blockThread.getBlockMutex().f("main");
thread.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("main has finished ");
}
}
运行结果
main lock acquired in f()
waiting lock in f()
main has finished
猜想成立