我有一段代码:
AtomicReference<List<String>> atomicStrings = new AtomicReference<>();
atomicStrings.set(someFunc());
Thread.sleep(10000);
System.out.print(String.join(",", atomicStrings.get()); // will this print a,b,c ?
哪里
private List<String> someFunc() {
List<String> list = new ArrayList<>();
new Thread(() -> {
try {
list.add("a");
Thread.sleep(1000);
list.add("b");
Thread.sleep(1000);
list.add("c");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
return list;
}
当然这是一个非常糟糕的例子,但是我试图通过添加延迟来模拟我的真实测试用例。我这里的问题是,由于somefunc()立即返回数组,数组元素被填充到另一个线程中,但是我们得到的结果存储在一个原子引用中,直到稍后才得到值,因此我在main函数中添加的延迟比生成新线程所需的延迟要大。我返回的数组是否会被填充所有元素?
2条答案
按热度按时间nwnhqdif1#
如果我们给atomicreference分配一个返回数组的函数,它的值是否会延迟设置?
首先,
AtomicReference.set()
是直接的,决不懒惰。如果我们看一下你的代码,就会发现someFunc()
返回一个ArrayList
所以这将立即被设置为atomicStrings
. 不幸的是,字符串是由另一个线程添加到列表中的,并且正在运行的主线程之间没有同步someFunc()
创建列表,以及向列表中添加字符串的线程。每当两个不同的线程访问同一个对象时,尤其是对该对象进行变异时,您需要担心互斥锁(竞争条件)和内存同步。有一件事你可以用来解决你的具体问题是使用
BlockingQueue
它是一个同步类而不是ArrayList
.BlockingQueue
注意所有的内存同步和互斥锁,以确保从多个线程进行的访问是正确的。然后,当内部线程调用
queue.add("a");
10秒后,主线程调用queue.iterator()
,它们将看到相同的字符串集合。没有必要这样做AtomicReference
因为主线程和内线程将共享的原子类是ArrayBlockingQueue
.9w11ddsr2#
你不是在给它“分配一个函数”,而是在立即计算
someFunc
并将值(对列表的引用)放置在AtomicReference
.原子类有特殊的happens before约束,因此
someFunc
保证对从引用中检索列表的任何人都可见,但是您在派生线程中对列表的修改与程序的其余部分没有“发生在”关系。行为未定义,包括ConcurrentModificationException
.