如何仅通过执行关键部分(在Lock内)来阻塞所有线程

时间:2019-05-24 13:28:57

标签: java multithreading

我有一个 MyClass 类,其中包含一个静态方法 GetObject()。多个线程正在调用 GetObject()。如果 int count 小于3,则使用旧对象(在类成员中创建)。

假设线程t4,t5,t6进入ELSE块。

我被困在ELSE条件块中,其中:

  1. 我只想为进入 lock()的第一个线程(t4)创建一次 new Object()(t5和t6仍然被阻止)
  2. 在第一个线程(t4)解锁后,其余被阻塞的线程(t5&t6)应该通过第一个线程(t4)返回新创建的对象,而不是为每个线程创建 new Object
  3. li>

我不希望其他被阻塞的线程创建 new Object()

我也尝试过使用Condition

我尝试了以下代码。

public Class MyClass  //Singleton - persistent data class.
{
    static MyClass s_instance;   //Singleton instance
    Object myObject = new Object();    
    private int count = 0;
    private final Lock myLock = new ReentrantLock();

    public MySystem()
    {
        s_instance = this;
        System.out.println("Constructor finished");
    }    

    public static Object GetObject()
    {
        if(s_instance.count < 3)
        {
            s_instance.count++;
            System.out.println("FROM IF " + s_instance.myObject);
            return s_instance.myObject;
        }
        else
        {
            s_instance.myLock.lock();        <----- All threads blocked here
            try{
                 Thread.sleep(2000);   //Performing database operations here.      
                 System.out.println("Creating new Object");          
                 s_instance.myObject = new Object();      //Assigning new Object to Singleton class.      
            }catch(InterruptedException ex) {
                    //Handling exception HERE
            } finally {
                s_instance.myLock.unlock();
            }

           System.out.println("FROM ELSE " + s_instance.myObject);
        }
        return s_instance.myObject;
     }
}

Expected Output from logs:
FROM IF java.lang.Object@aa940f3   <--- Old object
FROM IF java.lang.Object@aa940f3   <--- Old object
FROM IF java.lang.Object@aa940f3   <--- Old object
Creating new Object
FROM ELSE java.lang.Object@64fed39d   <--- Thread t4 New object
FROM ELSE java.lang.Object@64fed39d   <--- Object created by Thread t4.
FROM ELSE java.lang.Object@64fed39d   <--- Object created by Thread t4.

2 个答案:

答案 0 :(得分:0)

您的GetObject()方法中的以下代码不是线程安全的,因为2个线程可以同时评估if(s_instance.count <3)条件,然后它们两个都将增加s_instance.count ++,可能导致计数器达到a值大于3:

//NOTE: this is not thread safe
if(s_instance.count < 3)
{
    s_instance.count++;
    System.out.println("FROM IF " + s_instance.myObject);
    return s_instance.myObject;
}

此外,由于有问题的代码块总是从GetObject()方法返回,因此不需要else语句。为了使GetObject()方法线程安全,我建议进行以下更改:

public static Object GetObject() {
    do {
        synchronized(s_instance) {
            if(s_instance.count < 3) {
                //We can safely increment here, and the return will break us out of do/while loop
                s_instance.count++;
                System.out.println("FROM IF " + s_instance.myObject);
                return s_instance.myObject;
            }
        }

        //Now, the only way execution reaches here is if the count reached 3
        s_instance.myLock.lock();

        //Because multiple threads could reach here, we only want the first one
        //to create a new instance, all the others should loop
        if(s_instance.count == 3) {
            try {
                Thread.sleep(2000);   //Performing database operations here.      
            }
            catch(InterruptedException ex) {
             //Handling exception HERE
            } 
            finally {
                //Moved the logic to finally block so even if sleep was interrupted, a new instance will be constructed
                System.out.println("Creating new Object");          
                s_instance.myObject = new Object();      //Assigning new Object to Singleton class.
                s_instance.count = 0; //don't forget to reset counter
                s_instance.myLock.unlock();
                return s_instance.myObject; //return from here to break out of do/while loop
            }
       }
       else {
           //We weren't the first thread to want a new object, so release the
           //lock and the while loop will let redo the increment logic
           s_instance.myLock.unlock();
       }
   } while(true); //Keep looping until we are successful

}

答案 1 :(得分:-1)

您可以使用简单的Java监控器机制。不必使用第二个Object属性,可以使用flag。

public class MyClass  //Singleton - persistent data class.
{
  static MyClass s_instance;   //Singleton instance
  Object myObjectForFirstThree = new Object();
  Object myObjectForOthers;
  private int count = 0;

  static final Object monitor = new Object();

  public MyClass() {
    s_instance = this;
    System.out.println("Constructor finished");
  }

  public static Object getObject() {
    if (s_instance.count < 3) {
      s_instance.count++;
      System.out.println("FROM IF " + s_instance.myObjectForFirstThree);
      return s_instance.myObjectForFirstThree;
    } else {
      synchronized (monitor) {
        try {
          if (s_instance.myObjectForOthers == null) {
            Thread.sleep(2000);   //Performing database operations here.
            System.out.println("Creating new Object");
            s_instance.myObjectForOthers = new Object();      //Assigning new Object to Singleton class.
          }
        } catch (InterruptedException ex) {
          //Handling exception HERE
        }
      }
      System.out.println("FROM ELSE " + s_instance.myObjectForOthers);
      return s_instance.myObjectForOthers;
    }
  }

  public static void main(String[] args) {
    new MyClass(); // create s_instance
    for (int i = 0; i < 6; i++) {
      MyClass.getObject();
    }
  }
}