我正在关注 here 中的示例
我已将 processCommand
修改为-
private void processCommand() throws InterruptedException {
this.command = "xyz";
}
完整代码-
import java.util.logging.Level;
import java.util.logging.Logger;
public class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(Thread.currentThread().getName() + " Commad at start :" + command);
try {
processCommand();
} catch (InterruptedException ex) {
}
System.out.println(Thread.currentThread().getName() + " Command after processCommand : " + command);
}
private void processCommand() throws InterruptedException {
this.command = "xyz";
}
}
现在,我希望看到同步问题,对吗?基本上,当
System.out.println(Thread.currentThread().getName()+' Start. Command = '+command);
被执行后,它可以获取值xyz
,对吧?但我从来没有看到它。我在 Thread.Sleep 中尝试了各种值。
那么在这种情况下,是什么让 this.command = "xyz";
语句成为线程安全的呢?
我是这样开始线程的-
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}
最佳答案
更新
它仍然不完全是完整程序的样子......但根据我的想法,我看不出它有任何不线程安全的地方。
command
赋值有两点,值读取有两点。
- 主线程在构造函数中分配
command
。 - 第二个线程在调用
processCommand
之前读取run()
中的command
。 - 第二个线程在
processCommand
中分配command
- 第二个线程调用
processCommand
后,在run()
中读取command
。
最后三个事件发生在同一个线程上,因此不需要同步。第一个和第二个事件发生在不同的线程上,但此时主线程和工作线程之间应该存在“发生在之前”的关系。
如果主线程要
start()
第二个线程,那将提供之前发生的事情。 (JLS 是这么说的。)但实际上我们是使用
ThreadPoolExecutor.execute(Runnable)
来做交接,并且根据javadoc对于执行器
:Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread.
总而言之,所有 4 个感兴趣的事件都已正确同步,并且没有涉及 command
的竞争条件。
但是,即使这不是线程安全的,您也很难证明这种非线程安全的行为。
您无法证明它的主要原因是实际的不安全性是由于 Java 内存模型造成的。对
command
变量的更改只需要在存在同步点或建立“之前发生”的情况下刷新到主内存。但无论如何它们都可以被刷新……而且它们通常是……特别是如果有足够长的时间间隔,或者系统调用导致上下文切换。在这种情况下,您同时拥有两者。第二个原因是
System.err
和System.out
对象在内部是同步的,如果您不注意调用它们的方式您可以消除您试图演示的线程安全问题。
这是关于涉及非同步访问共享变量的线程安全问题的“问题”。实际的竞争条件通常涉及非常小的时间窗口;即两个事件需要在几个时钟周期(当然少于一微秒)内发生,以便注意到竞争。这可能很少发生,这就是为什么涉及竞争条件的问题通常很难重现。
关于java线程同步-这不应该工作,但它是:) -,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16867337/