我正在关注here
中的示例我修改了processCommand
as-
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);
执行, CAN 获取值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);
}
答案 0 :(得分:4)
您在此处未看到竞争条件的原因是
Runnable worker = new WorkerThread('' + i);
竞争条件涉及共享资源。另一方面,所有工作线程都在更改自己的私有成员command
。为了引发竞争条件,你需要做一些像
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread('' + 0);
executor.execute(worker);
worker.setCommand('' + i);
}
现在,当工作人员尝试访问command
字段时,它可能会获得陈旧的0
值或i
值。
答案 1 :(得分:4)
的更新强> 的
它仍然不完全是整个程序的样子......但根据我的想法,我看不出它不是线程安全的任何一点。
有两个点分配command
,两个点读取值。
command
。command
之前,第二个主题会在run()
中读取processCommand
。command
processCommand
command
后在run()
中读取processCommand
。最后三个事件发生在同一个线程上,因此不需要同步。第一个和第二个事件发生在不同的线程上,但是应该是&#34;发生在&#34;之前。此时主线程和工作线程之间的关系。
如果主线程是start()
第二个线程,那将提供之前发生的事情。 (JLS这样说。)
但实际上我们正在使用ThreadPoolExecutor.execute(Runnable)
进行移交,并根据Executor
的{{3}}:
内存一致性效果:在将Runnable对象提交给Executor之前,线程中的操作发生在执行开始之前,可能是在另一个线程中。
总之,所有感兴趣的4个事件都已正确同步,并且没有涉及command
的竞争条件。
但是,即使这不是线程安全的,您也很难演示非线程安全的行为。
您无法证明的主要原因是实际的非安全性是由于Java内存模型造成的。如果存在同步点或某些事情以确定&#34;在&#34;之前发生,则仅需要将command
变量的更改刷新到主存储器。但无论如何它们都可以刷新......而且它们通常都是......特别是如果有足够长的时间间隔或系统调用导致上下文切换。在这种情况下,你有两个。
第二个原因是System.err
和System.out
对象在内部同步,如果您不小心调用它们,则可以消除您尝试的线程安全问题演示。
这是&#34;事情&#34;关于涉及对共享变量的非同步访问的线程安全问题。实际的比赛条件通常涉及非常小的时间窗口;即在需要注意的比赛中,在几个时钟周期内(当然小于一微秒)内发生需要的两个事件。这很可能发生很少,这就是为什么涉及竞争条件的问题通常难以重现的原因。