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

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

解决方法:

UPDATE

它仍然不完全是整个程序的样子……但基于我的想法,我看不出它不是线程安全的任何一点.

分配命令有两个点,读取值有两个点.

>主线程在构造函数中指定命令.
>第二个线程在调用processCommand之前在run()中读取命令.
>第二个线程在processCommand中分配命令
>第二个线程在调用processCommand后在run()中读取命令.

最后三个事件发生在同一个线程上,因此不需要同步.第一个和第二个事件发生在不同的线程上,但在那一点上主线程和工作线程之间应该存在“先发生”关系.

>如果主线程要启动()第二个线程,那将提供之前发生的事情. (JLS这样说.)
>但实际上我们正在使用ThreadPoolExecutor.execute(Runnable)进行移交,并根据javadoc for Executor:

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个事件都是正确同步的,并且没有涉及命令的竞争条件.

但是,即使这不是线程安全的,您也很难证明非线程安全的行为.

>无法证明它的主要原因是实际的非安全性是由Java内存模型引起的.如果存在同步点或某些事情以确定“之前发生”,则只需将命令变量的更改刷新到主存储器.但无论如何它们都可以刷新……而且它们通常都是……特别是如果有足够长的时间间隔或系统调用导致上下文切换.在这种情况下,你有两个.
>第二个原因是System.err和System.out对象在内部同步,如果您不小心调用它们,则可以消除您尝试演示的线程安全问题.

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

上一篇:java – Spring 3为ThreadPoolTask​​Executor设置ThreadFactory


下一篇:java – 在循环中将Runnable提交给线程池是一种好习惯吗?