[英]Is a volatile reference a safe way to pass MotionEvents between threads?
[英]Correct way to pass a POJO (no volatile fields,synchronized methods) between multiple threads and assure visibility of changes?
我发现了很多与可见性、同步和线程相关的问题和答案,但似乎都没有涵盖我的特定用例(或者我可能只是不擅长搜索;-)所以我会问一个新问题并希望一些慷慨的灵魂会启发我:)
我的问题是:鉴于下面的代码,访问主线程中 WorkItem 项的字段是否会正确反映线程池工作线程对它们所做的任何更改?
我的怀疑是“否”,因为这感觉类似于传递一个包含一些值的数组,只是在数组引用上同步,而不是在单个元素上同步...... JDK 中存在像 AtomicReferenceArray 这样的类以及它们为什么存在一定是有原因的访问单个元素时使用 getVolatile()/setVolatile()。
package com.voipfuture.voipmng.monitoring;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class IsThisThreadSafe
{
public static class WorkItem { public String field1,field2; /* etc. */ }
public static void main(String[] args) throws InterruptedException
{
final ExecutorService service = Executors.newFixedThreadPool(5);
final List<WorkItem> items = List.of(new WorkItem(), new WorkItem());
final CountDownLatch finished = new CountDownLatch(items.size());
for (WorkItem item : items)
{
service.submit(() ->
{
try
{
synchronized (item)
{
// mutate object
item.field1 = "test";
}
}
finally
{
finished.countDown();
}
});
}
finished.await();
for (WorkItem item : items)
{
// will this make sure all changes done inside
// threadpool worker threads are visible here ?
synchronized (item)
{
// do stuff with work item
System.out.println(item.field1);
}
}
}
}
如果所有线程对共享 object 的所有访问(读取和写入)都在同步块中完成,则访问是安全的。 同步块添加了 memory 屏障,因此任何等待写入主 memory 的修改都会被提交。
鉴于下面的代码,访问主线程中 WorkItem 项的字段是否会正确反映线程池工作线程对它们所做的任何更改?
答案实际上是肯定的,你会的。 确保可视性有两个原因。
synchronized(item)
:假设你没有CountDownLatch
(更多关于下一个),但你只有synchronized(item)
在那里。 由于您正在同步同一实例的读取和写入,因此读取线程将看到写入之前发生的更改。 但是,如果没有发生写入,那么您将看不到值test
,您只会看到null
。 因此,即使它保证 memory 可见性,它也不能保证程序顺序。
finished.await();
现在让我们假设您删除了synchronized(item)
。 如果您所拥有的只是finished.countDown()
和finished.await()
,那么您保证在await
继续之前发生的任何更新现在在退出await
之后都是可见的。 因此,只需使用CountDownLatch
,您就可以为程序提供 memory 可见性和并发程序顺序。
Memory 一致性效果:在计数达到零之前,线程中的操作在调用 countDown() 之前发生在从另一个线程中的相应 await() 成功返回之后的操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.