我对Java中的多线程很新.因为我需要一个线程安全的单例(我实现为枚举),我写了一个小的测试代码,它会产生一个奇怪的输出.
代码:
public enum EnumSingleton { INSTANCE; /** state variables */ private String message; /** Constructor */ private EnumSingleton() { } /** add well-known accessor for the instance (is NOT necessary) */ public static EnumSingleton getInstance() { return INSTANCE; } /** Accessors */ public String getMessage() { return message; } public void setMessage(String name) { this.message = name; } } public class App { public static void main(String[] args) { for (int i = 0; i < 10; i++) { final int b = i; Thread thread = new Thread("Thread #" + b) { @Override public void run() { EnumSingleton singleton = EnumSingleton.getInstance(); singleton.setMessage("Message written by "+this.getName()); System.out.println("Current thread "+this.getName() + ": "+singleton.getMessage()); } }; thread.start(); } } }
因此,每个线程将其名称写入枚举的属性"message"中,然后将其打印到STDOUT.我得到以下输出,我觉得很奇怪:
Current thread Thread #6: Message written by Thread #3 Current thread Thread #1: Message written by Thread #1 Current thread Thread #8: Message written by Thread #8 Current thread Thread #5: Message written by Thread #1 Current thread Thread #4: Message written by Thread #4 Current thread Thread #9: Message written by Thread #9 Current thread Thread #7: Message written by Thread #3 Current thread Thread #2: Message written by Thread #3 Current thread Thread #0: Message written by Thread #3 Current thread Thread #3: Message written by Thread #3
我期望的是我得到每个循环计数器(0-9)一条消息.但是在这个例子中我有多个由Thread#3写的消息,那怎么可能呢?有竞争条件吗?
如果我的代码是废话:我如何正确测试我的单例以获得线程安全?
这里有一个明确的竞争条件,因为你message
在枚举的单例实例中有一个变量.你的线程都在同时写入和读取该变量,所以你希望看到这样的结果.
枚举构造意味着单个对象的创建是线程安全的,但是仍然需要正确处理对其中的方法的调用.
你正在寻找的方法是message
成为一个线程局部变量,或者将消息的设置和它的读取放在一个单独的synchronized
块中,可能锁定单例对象.
单例不是线程安全的 - 你没有做任何事情来保证message
变量的可见性.
你可以做到这volatile
一点.但请注意,输出可能是许多不同的东西-特别是你不一定会得到一个Message written by Thread #i
每个i
.