简体   繁体   English

通过更改运行时监视器将两个线程放入同步块

[英]Two threads into synchronized block by changing monitor in run time

Note: Even though below scenariois are not valid which violates concept of synchronized block , still I tried to know how it works if so 注意:即使下面的方案无效,这违反了同步块的概念,我还是想知道它如何工作

Created two threads , both threads tries to executes same critical section , suprisingly both threads enters into critical section even though by changing monitor. 创建了两个线程,两个线程都试图执行相同的关键部分,令人惊讶的是,即使通过更改监视器,两个线程也都进入了关键部分。

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);
        }
    }

}

Below is output: 输出如下:

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

Seems both Threads enters even though monitor is changed. 即使更改了监视器,似乎两个线程都进入了。

Any Help is appreciated.. 任何帮助表示赞赏。

As I mentioned in my comment, your getMonitor method is a big race condition, because you are not synchronizing on the map object so between the time that you check if the key exists and the time that you put a new object in, the other thread can do the same. 正如我在评论中提到的,您的getMonitor方法是一个很大的竞争条件,因为您没有在map对象上进行同步,因此在检查键是否存在的时间与放入新对象的时间之间,另一个线程可以做同样的。

However, since you wait one second before starting the second thread, that is not the issue here. 但是,由于在启动第二个线程之前要等待一秒钟,所以这不是问题。

There issue is that you are using the ConcurrentHashMap.contains(Object) method, which checks if the value exists, not if the key exists like you want. 问题在于您使用的是ConcurrentHashMap.contains(Object)方法,该方法检查值是否存在,而不是是否如您所愿存在。 You need to change the method to: 您需要将方法更改为:

private Object getMonitor(String id){
    synchronized (objMap) {
        if (objMap.containsKey(id)) { // <---- containsKey(...), not contains(...)
            return objMap.get(id);
        } else {
            objMap.put(id, new Object());
            return objMap.get(id);
        }
    }
}

Also, you could have avoided your incorrect conclusion that your monitor was locked twice by different thread by actually checking which monitor you locked: 另外,通过实际检查锁定的监视器,您可以避免错误的结论:监视器被不同的线程锁定了两次:

System.out.println(
    "Thread Entered Critica section is:" + id + " and name is:"
    + name + " and monitor is: " + monitor);

Problem is with your getMonitor method. 问题出在您的getMonitor方法上。 Changing it has following will fix issue. 进行以下更改将解决问题。

private Object getMonitor(String id){
    objMap.putIfAbsent(id, new Object());
    return objMap.get(id);
}

The reason is that your original getMonitor method is prone for Race Condition Issues. 原因是您的原始getMonitor方法易于出现“竞争状况问题”。 The common misconception is that using Thread Safe collections like Vector , ConcurrentHashMap inherently makes your code Thread Safe but it won't. 常见的误解是使用VectorConcurrentHashMap类的线程安全集合会固有地使您的代码成为线程安全的,但事实并非如此。

Your getMonitor has a classic Check-Then-Act style of coding (if else) and your objMap is defined as a static variable so all threads will be accessing the same instance. 您的getMonitor具有经典的Check-Then-Act编码风格(如果没有),并且objMap被定义为静态变量,因此所有线程都将访问同一实例。

With the changes I proposed ( objMap.putIfAbsent ) the race condition can be avoided because Check-Then-Act will now be done inside the safety of the Locking mechanism of objMap 通过我建议的更改( objMap.putIfAbsent ),可以避免竞争条件,因为现在将在objMap锁定机制的安全范围内完成Check-Then-Act

The change prints the following 更改打印以下内容

Thread Entered Critica section is:1 and name is:T1
Thread Exiting Critical 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:T2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM