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

标签 java multithreading thread-safety threadpool

我正在关注 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赋值有两点,值读取有两点。

  1. 主线程在构造函数中分配command
  2. 第二个线程在调用 processCommand 之前读取 run() 中的 command
  3. 第二个线程在processCommand中分配command
  4. 第二个线程调用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.errSystem.out 对象在内部是同步的,如果您不注意调用它们的方式您可以消除您试图演示的线程安全问题。


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

关于java线程同步-这不应该工作,但它是:) -,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16867337/

相关文章:

java - 在 TreeSet 中,基于不同属性的自定义对象的排序和唯一性

c - pthread_join 导致段错误(简单程序)

thread-safety - Grails HTTPBuilder 线程安全吗?

swing - 线程 "AWT-EventQueue-0"java.lang.ClassCastException : javax. swing.JTable 中的异常

java - FXML、脚本标签和初始化方法

找不到 Java 文件,但在同一目录中并且捕获了异常

java - Java 中多线程同步不起作用

使用 100% 单 CPU 内核的 Java Web 应用程序

c# - 这种使用通用 List 线程安全吗

thread-safety - 从工作线程传送输出样本的 DirectShow 过滤器的线程安全