3

在 Java Concurrency In Practice 中,作者指出

  1. 不可变对象可以通过任何机制发布
  2. 任何线程都可以安全地使用不可变对象,而无需额外的同步,即使不使用同步来发布它们也是如此。

这是否意味着以下成语可以安全地发布不可变对象?

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 添加的列表中看到不可变对象)

非常感谢你。


此外,作者说如果 Resource 是不可变的,则以下代码是安全的。

@NotThreadSafe
public class UnsafeLazyInitialization {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null)
            resource = new Resource();  // unsafe publication
        return resource;
    }
}

第 16.3 节 初始化安全的保证允许正确构造的不可变对象在线程之间安全共享而无需同步,无论它们如何发布,即使使用数据竞争发布。(这意味着unsafeLazyInitialization如果它是不可变的,它实际上是安全Resource的。)

对于本题的第二部分,在另一个问题中详细讨论(点击这里

4

4 回答 4

5

是的,你是对的,存在数据竞赛。

只有ImmutableObject是不可变的并且可以在线程之间安全共享List,但是您的 没有这些相同的保证,因此在添加ImmutableObject和检索它之间存在数据竞争。

在 JCIP 中,作者的意思是不可变对象可以安全地发布,因为您不必担心制作防御性副本之类的事情。

至于:

任何线程都可以安全地使用不可变对象,而无需额外的同步,即使不使用同步来发布它们也是如此。

这个声明意味着给定两个线程A,它们都通过任何方式获得了一个不可变对象,它们都可以使用对象A而不用担心线程安全问题。

于 2013-01-31T02:39:57.707 回答
3

您的List<ImmutableObject> list容器对象不是不可变的。因此,对其添加和获取方法将不是线程安全的。这些方法需要同步才能从多个线程进行并发访问。

于 2013-01-31T02:43:50.063 回答
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 退出

于 2013-01-31T03:19:27.243 回答
0

是的,数据竞争肯定存在。这是一种情况:

虽然 ThreadA在里面methodA,然后 ThreadB将被执行methodB,但不能保证methodA之前已经返回methodB。如果不幸的是,methodB已经返回而methodA尚未返回,则很有可能获得IndexOutOfBoundsException

// thread A invokes this method first
public static void methodA () {
    //assume control is taking time at this point, while thread B already returned!!!

    list.add(new ImmutableObject());
}

// thread B invokes this method later
public static ImmutableObject methodB () {
    return list.get(0);
}
于 2015-11-14T17:58:03.333 回答