从Memory Consistancy Property,我们知道: “在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或删除该元素之后的操作之前。”
这是否意味着:如果我创建一个对象并将其放入一个线程中的ConcurrentLinkedQueue,另一个线程将看到该对象的所有属性而没有对象的其他同步?
例如:
public class Complex{
int index;
String name;
public Complex(int index, String name){
this.index = index;
this.name = name;
}
public String getName(){
return name;
}
public int getIndex(){
return index;
}
}
public class SharedQueue{
public static ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
}
在一个主题中:
...........
Complex complex = new Complex(12, "Sam");
SharedQueue.queue.add(complex);
...........
另一个帖子中的
......
Complex complex = SharedQueue.queue.poll();
System.out.printly(complex.getIndex() + ";" + complex.getName());
.............
第二个线程肯定会看到complex
对象的属性吗?如果第二个线程碰巧获取对象并在第一个线程将对象放入队列后打印它。
我们知道在正常情况下我们应该在多线程环境中同步对象(如果它是共享的。)
像
public class Complex{
int index;
String name;
public Complex(int index, String name){
this.index = index;
this.name = name;
}
public synchronized String getName(){
return name;
}
public synchronized int getIndex(){
return index;
}
}
答案 0 :(得分:1)
所有线程都会看到他们可以获取引用的所有对象,所以是的,这也适用于集合。并发集合使用同步不会因两个或多个线程同时访问它们而中断(例如,for循环可能会中断),因此您可以使用它们在线程之间共享对象。
如果你的对象是不可变的,即只读,作为上面的Complex类(添加final修饰符),那么你不需要同步对它的访问,因为它是线程安全的。
同步点是为了确保您的变量在n次操作中保持一致。例如:
如果你想计算
i = i + 2;
然后这包括首先读取i的值,加2,然后将值设置回i。
现在,如果其他一些线程在你添加2后立即更新,那么你实际上会覆盖这个更新,因为你仍然保持基于前一个值+ 2的总和,将其设置回i。
答案 1 :(得分:0)
JSR 133还指定Object的 final 字段是完整的,并且在构造函数完成时对所有线程都可见。这种支持是在Java 5.0(2004年)中添加的。您的对象无需其他操作即可在所有线程中显示和更正。
在不可变对象上使用synchronized在这种情况下无法实现。
有很多文件说明其他情况,这些文件通常早于2004年。;)
答案 2 :(得分:0)
完全。在这种情况下,ConcurrentLinkedQueue
用作同步器。
答案 3 :(得分:0)
这意味着如果线程A将对象放入集合中,并且线程B从那里获取它,则线程B可以在将对象放入集合之前查看线程A完成的任何操作的结果(以及发生在之前的其他线程中等等)。这些操作包括对象的初始化(如果它是由线程A完成的),这样线程B就可以看到对象处于一致状态。
请注意,它不会对将对象放入集合后的线程A中发生的操作或其他线程的操作提供任何保证(除了与传递在之前发生的操作之外的操作,因为如上所述,如果要在将对象放入集合后修改对象,则仍需要同步。
顺便说一句,您的“正常情况”示例已损坏,因为构造函数中的操作未同步。