我想测试延迟初始化是否是线程安全的,所以我的代码如下:
package LazyInit;
import java.util.Random;
public class UnThreadSafeLazyInit {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (null == instance) {
instance = new ExpensiveObject();
}
System.out.println("instance=" + instance);
return instance;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
UnThreadSafeLazyInit init = new UnThreadSafeLazyInit();
Task t1 = init.new Task();
Task t2 = init.new Task();
t1.start();
t2.start();
try {
Thread.sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(t1.getInstance() == t2.getInstance());
}
}
static class ExpensiveObject {
}
class Task extends Thread {
private ExpensiveObject instance = null;
private Random rand = new Random(47);
public void setInstance () {
this.instance = UnThreadSafeLazyInit.this.getInstance();
}
public ExpensiveObject getInstance() {
return instance;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(rand.nextInt(1000));
} catch (Exception e) {
e.printStackTrace();
}
setInstance();
}
}
}
在我的代码中,每当我new
两个Thead任务调用public ExpensiveObject getInstance()
时,为了证明两个instance
可能与竞争条件后的ExpensiveObject不同。
当我运行它时,它总是由true
返回t1.getInstance() == t1.getInstance()
。
据我所知,如果我不synchronized
函数public ExpensiveObject getInstance()
,它可能会返回false,因为在Lazy Initialization中存在种族条件。
我需要找出哪个代码是错误的。
谢谢。
答案 0 :(得分:2)
通过检查代码,它不是线程安全的。你遇到的问题是延迟几毫秒是计算机的一个巨大时间,你很可能不会发现这种类型的测试有问题。
例如,更新易失性字段与其他线程可见之间的典型延迟约为5纳秒。大约这么久,您的解决方案不是线程安全的。您正在等待高达1,000,000,000纳秒,以查看是否出现问题。
这就像试图查看烟花持续5秒钟是否会消失,但在结束之前317年闭上眼睛没有烟火。
答案 1 :(得分:1)
其他人已经涵盖了为什么它不是线程安全的。但我想对你的标题发表评论:“我想测试延迟初始化是否是线程安全的”。
您无法测试一段代码是否是线程安全的。您可能能够找到一个证明它不是的测试,但测试只能证明线程安全:
System.out.println
已同步)答案 2 :(得分:0)
最简单的方法是使ExpensiveObject成为一个非常昂贵的对象:
public class ExpensiveObject {
public ExpensiveObject() {
System.out.println("I'm expensive!");
try {
Thread.sleep(2000L);
}
catch (InterruptedException e) {
}
System.out.println("See. It took 2 seconds to create me!");
}
}
否则,进入rece条件的可能性非常小,特别是因为一个线程在另一个线程之后启动,因此在另一个线程之后调用setInstance()
。
答案 3 :(得分:0)
这不是线程安全的。你这次很幸运。修改你的代码:
public ExpensiveObject getInstance() {
if (null == instance) {
System.out.println("old instance=" + instance);
instance = new ExpensiveObject();
System.out.println("new instance=" + instance);
}
return instance;
}
// In main
Thread.sleep(40); // Thread.sleep(4000);
// In run
Thread.sleep(rand.nextInt(10)); // Thread.sleep(rand.nextInt(1000));
我在控制台中看到了很多false
的代码。