java - 将对象分配给在同步块(synchronized block)外定义的字段 - 它是线程安全的吗?

标签 java multithreading concurrency thread-safety

难道这段java代码的线程安全有什么问题吗?线程 1-10 通过 sample.add() 添加数字,线程 11-20 调用 removeAndDouble() 并将结果打印到标准输出。我回想起有人说过,在同步块(synchronized block)之外使用它的 removeAndDouble() 中使用它的方式分配项目可能不是线程安全的。编译器可能会优化指令,使它们乱序出现。这里是这样吗?我的 removeAndDouble() 方法不安全吗?

从并发的角度来看,这段代码还有什么问题吗?我正在尝试使用 Java(1.6 以上版本)更好地理解并发性和内存模型。

import java.util.*;
import java.util.concurrent.*;

public class Sample {

    private final List<Integer> list = new ArrayList<Integer>();

    public void add(Integer o) {
        synchronized (list) {
            list.add(o);
            list.notify();
        }
    }

    public void waitUntilEmpty() {
        synchronized (list) {
            while (!list.isEmpty()) {
                try { 
                    list.wait(10000);  
                 } catch (InterruptedException ex) { }
            }
        }
    }

    public void waitUntilNotEmpty() {
        synchronized (list) {
            while (list.isEmpty()) {
                try { 
                    list.wait(10000);  
                 } catch (InterruptedException ex) { }
            }
        }
    }

    public Integer removeAndDouble() {
        // item declared outside synchronized block
        Integer item; 
        synchronized (list) { 
            waitUntilNotEmpty();
            item = list.remove(0);
        }
        // Would this ever be anything but that from list.remove(0)?
        return Integer.valueOf(item.intValue() * 2);
    }

    public static void main(String[] args) {
        final Sample sample = new Sample();

        for (int i = 0; i < 10; i++) {
            Thread t = new Thread() {
                public void run() {
                    while (true) {
                        System.out.println(getName()+" Found: " + sample.removeAndDouble());
                    }
                }
            };
            t.setName("Consumer-"+i);
            t.setDaemon(true);
            t.start();
        }

        final ExecutorService producers = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            final int j = i * 10000;
            Thread t = new Thread() {
                public void run() {
                    for (int c = 0; c < 1000; c++) {
                        sample.add(j + c);
                    }
                }
            };
            t.setName("Producer-"+i);
            t.setDaemon(false);
            producers.execute(t);
        }

        producers.shutdown();
        try {
            producers.awaitTermination(600, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        sample.waitUntilEmpty();        
        System.out.println("Done.");
    }
}

最佳答案

对我来说它看起来是线程安全的。这是我的推理。

每次访问 list 都是同步的。这很棒。即使您在 item 中提取了 list 的一部分,该 item 也不会被多个线程访问。

只要您只在同步时访问 list,就应该很好(在您当前的设计中。)

关于java - 将对象分配给在同步块(synchronized block)外定义的字段 - 它是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3781694/

相关文章:

cocoa - NSOperation子类cancel的正确编程方法

java - 提交给执行者的 FutureTask 不运行

java - 什么是静态异常检查和动态异常检查?

java - Spring 框架项目未加载?

静态资源的C++线程安全

c# - 并发词典的正确用法

c - 尝试在 c 中实现并发 TCP 服务器和客户端

java - 在哪里可以看到 java web start 应用程序中抛出的异常?

java - 如何设置 Swing 计时器在启动时执行

java - 如何创建 Cassandra ITrigger 析构函数?