java thread safe:它是线程安全的吗?

时间:2017-05-18 02:02:17

标签: java multithreading

我在实践中阅读java并发。有一些问题,我无法理解。 例如,

package com.thread;

import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class HiddenIterator {
    private final Set<Integer> set = Collections.synchronizedSet(new HashSet<Integer>());
    public void add(Integer i) {
        synchronized (set) {
            set.add(i);
        }
    }
    public void remove(Integer i) {
        synchronized (set) {
            set.remove(i);
        }
    }
    public void addTenThings() {
        Random random = new Random();
        for (int i = 0; i < 10; ++i) {
            add(random.nextInt());
        }
        //Hidden Iterator!  
        System.out.println("DEBUG: added ten elements to " + set);
    }
}

程序线程安全吗? 如果不是,如何编辑?

2 个答案:

答案 0 :(得分:2)

您对该集合的部分访问权限是线程安全的:对addremove的调用是同步的,因此它们无法同时运行。

但是,在构建消息时,最后的System.out行会在集合上调用toString,而toString必须遍历集合的元素。虽然您已经使用了synchronizedSet,但它只能保护对各个元素的访问权限 - 但不会在迭代期间保持集合不变。如果其他线程在System.out行运行时添加和删除元素,则无法预测消息中将显示哪些数字。您需要围绕该行synchronized阻止&#34;冻结&#34;构建消息时设置的内容。

请注意,只有个人 add来电是同步的,因此其他线程可以看到&#34;之间的#34;要添加的各个项目。这意味着其他线程可能只看到十个项目中的一些列表。取决于您的程序使用列表的内容,这可能是也可能不是问题。

如果您需要以原子方式添加十个元素,以便其他线程可以看到所有这些元素,或者看不到它们,则可以在synchronized方法中围绕循环放置addTenThings块。

您不需要同时使用Collections.synchronizedSet synchronized块。其中一个还可以。不同之处是:

  • Collections.synchronizedSet保护对该集的所有访问权限,因此您无法在需要的地方忘记同步。但是,它只能保护集合上的单个方法调用。特别是,迭代该集会导致不可预测的结果,因为在循环运行时,其他线程可以添加和删除项目。
  • synchronized块可以保护集合上的多个方法调用,以便它们充当原子操作 - 但它们只能防止其他synchronized块,因此您必须记住使用{{1}围绕访问该集的所有代码。

答案 1 :(得分:1)

有些太安全了,有些不够安全。您不需要在add()remove()内明确同步,因为synchronizedSet包装器会自动完成同步。

但是,您确实需要围绕println()语句进行同步,因为当您连接set时,它会隐式调用set.toString(),它在内部迭代其元素(&#34;隐藏迭代器&#34;),如果没有明确的同步就不安全,如documentation中所述。