java线程同步 - 这不应该工作,但它是:) -

时间:2013-05-31 23:20:22

标签: java multithreading thread-safety threadpool

我正在关注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);
}

2 个答案:

答案 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,两个点读取值。

  1. 主线程在构造函数中指定command
  2. 在致电command之前,第二个主题会在run()中读取processCommand
  3. 第二个主题分配command
  4. 中的processCommand
  5. 第二个帖子在调用command后在run()中读取processCommand
  6. 最后三个事件发生在同一个线程上,因此不需要同步。第一个和第二个事件发生在不同的线程上,但是应该是&#34;发生在&#34;之前。此时主线程和工作线程之间的关系。

    • 如果主线程是start()第二个线程,那将提供之前发生的事情。 (JLS这样说。)

    • 但实际上我们正在使用ThreadPoolExecutor.execute(Runnable)进行移交,并根据Executor的{​​{3}}:

        

      内存一致性效果:在将Runnable对象提交给Executor之前,线程中的操作发生在执行开始之前,可能是在另一个线程中。

    总之,所有感兴趣的4个事件都已正确同步,并且没有涉及command的竞争条件。


    但是,即使这不是线程安全的,您也很难演示非线程安全的行为。

    • 您无法证明的主要原因是实际的非安全性是由于Java内存模型造成的。如果存在同步点或某些事情以确定&#34;在&#34;之前发生,则仅需要将command变量的更改刷新到主存储器。但无论如何它们都可以刷新......而且它们通常都是......特别是如果有足够长的时间间隔或系统调用导致上下文切换。在这种情况下,你有两个。

    • 第二个原因是System.errSystem.out对象在内部同步,如果您不小心调用它们,则可以消除您尝试的线程安全问题演示。


    这是&#34;事情&#34;关于涉及对共享变量的非同步访问的线程安全问题。实际的比赛条件通常涉及非常小的时间窗口;即在需要注意的比赛中,在几个时钟周期内(当然小于一微秒)内发生需要的两个事件。这很可能发生很少,这就是为什么涉及竞争条件的问题通常难以重现的原因。