注意:尽管下面的场景是无效的,这违反了synchronized block的概念,但我还是试图知道它是如何工作的
创建了两个线程,两个线程都试图执行相同的临界区,令人惊讶的是,两个线程都进入了临界区,即使通过更改监视器。
public class MultiThreadTest {
final static ConcurrentHashMap<String,Object> objMap = new ConcurrentHashMap<String, Object>();
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread(objMap,"1","T1"));
Thread t2 = new Thread(new MyThread(objMap,"1","T2"));
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(MultiThreadTest.class.getName()).log(Level.SEVERE, null, ex);
}
t2.start();
}
}
class MyThread implements Runnable{
private final ConcurrentHashMap<String,Object> objMap;
private final String id;
private final String name;
public MyThread(ConcurrentHashMap<String,Object> objMap, String id, String name){
this.objMap = objMap;
this.id =id;
this.name = name;
}
@Override
public void run() {
Object monitor = getMonitor(id);
synchronized(monitor){
System.out.println("Thread Entered Critica section is:"+id+" and name is:"+name);
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(MyThread.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Thread Exiting Critical section is:"+id+" and name is:"+name);
}
}
private Object getMonitor(String id){
if(objMap.contains(id)){
return objMap.get(id);
}else{
objMap.put(id,new Object());
return objMap.get(id);
}
}
}
输出如下:
Thread Entered Critica section is:1 and name is:T1
Thread Entered Critica section is:1 and name is:T2
Thread Exiting Critical section is:1 and name is:T1
Thread Exiting Critical section is:1 and name is:T2
似乎两个线程都进入,即使监视器已更改。
感谢您的帮助。。
2条答案
按热度按时间hs1rzwqc1#
问题是你的
getMonitor
方法。更改它有以下问题将解决。原因是你原来
getMonitor
方法容易出现竞争条件问题。常见的误解是使用线程安全的集合Vector
,ConcurrentHashMap
本质上使你的代码线程安全,但它不会。你的
getMonitor
有一个经典的检查,然后行为风格的编码(如果没有)和您的objMap
定义为静态变量,因此所有线程都将访问同一示例。我提议的改变(
objMap.putIfAbsent
)竞争条件可以避免,因为检查然后行动现在将在安全的锁止机构的内部进行objMap
更改打印以下内容fnatzsnv2#
正如我在评论中提到的,你的
getMonitor
方法是一个很大的竞争条件,因为您没有在map对象上同步,所以在检查键是否存在的时间和放入新对象的时间之间,另一个线程也可以这样做。但是,由于在启动第二个线程之前要等待一秒钟,所以这不是这里的问题。
问题是您正在使用
ConcurrentHashMap.contains(Object)
方法,它检查值是否存在,而不是键是否如您所希望的那样存在。您需要将方法更改为:另外,您可以通过实际检查锁定的监视器来避免错误的结论,即监视器被不同的线程锁定了两次: