我有一个 MyClass 类,其中包含一个静态方法 GetObject()。多个线程正在调用 GetObject()。如果 int count 小于3,则使用旧对象(在类成员中创建)。
假设线程t4,t5,t6进入ELSE块。
我被困在ELSE条件块中,其中:
我不希望其他被阻塞的线程创建 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.
答案 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();
}
}
}