在Java Concurrency In Practice中,作者声明
这是否意味着以下习语可以安全地发布不可变对象?
public static List<ImmutableObject> list = new ArrayList<ImmutableObject>();
// thread A invokes this method first
public static void methodA () {
list.add(new ImmutableObject());
}
// thread B invokes this method later
public static ImmutableObject methodB () {
return list.get(0);
}
会有任何数据竞争吗? (这意味着线程B可能无法在线程A添加的列表中看到不可变对象)
非常感谢。
更多,作者说如果资源是不可变的,以下代码是安全的。
@NotThreadSafe
public class UnsafeLazyInitialization {
private static Resource resource;
public static Resource getInstance() {
if (resource == null)
resource = new Resource(); // unsafe publication
return resource;
}
}
Section16.3 初始化安全性的保证允许在线程之间安全地共享正确构造的不可变对象而不进行同步,无论它们是如何发布的,即使使用数据争用发布也是如此。 (这意味着如果
unsafeLazyInitialization
是不可变的,Resource
实际上是安全的。)
对于此问题的第2部分,将在另一个问题中详细讨论(点击here)
答案 0 :(得分:4)
是的,你是对的,有数据竞争。
只有ImmutableObject
是不可变的并且可以在线程之间安全地共享,但是List
没有这些相同的保证,因此在添加ImmutableObject
之间存在数据竞争并检索它。
在JCIP中,作者认为不可变对象在发布时是安全的,因为您不必担心做出防御性副本等事情。
至于:
任何线程都可以安全地使用不可变对象而无需额外的 同步,即使不使用同步发布 它们。
这个语句意味着给定2个带有不可变对象A
的线程,它们都是通过任何方式获取的,它们都可以使用对象A
,而不必担心线程安全问题。
答案 1 :(得分:3)
您的List<ImmutableObject> list
容器对象不是不可变的。因此,对它的add和get方法将不是线程安全的。需要同步这些方法以便从多个线程进行并发访问。
答案 2 :(得分:1)
您的问题表明您正在预期第5.3节 - 阻止队列和生产者消费者模式。以下是使用阻塞队列的类似内容:
public class Blocking
{
private BlockingQueue<ImmutableObject> queue = new ArrayBlockingQueue<ImmutableObject>(10);
public void methodA() {
queue.add(new ImmutableObject());
}
public ImmutableObject methodB() throws InterruptedException
{
return queue.take();
}
static class ImmutableObject
{
}
}
阻塞队列是高度可变的 - 但是设计为线程安全的,因此您可以在不进行额外同步的情况下使用它。只要您传递的对象是不可变的,整个设计就是线程安全的。
在上面的示例中,methodB使用“take”,它将阻塞,直到调用methodA将某些内容放入队列中。或者直到线程被中断,此时它将通过InteruptedException
退出答案 3 :(得分:0)
是的,确实存在数据竞争的可能性。这是一种情况:
虽然线程A
在methodA
内,然后线程B
将执行methodB
,但无法保证methodA
在{{1}之前返回}}。不幸的是,如果methodB
尚未返回,methodB
尚未返回,则很有可能获得methodA
:
IndexOutOfBoundsException