确保一个线程的 "updates" 对 Java 中的其他线程是可读的
Making sure a thread's "updates" is readable to other threads in Java
我有一个主线程将启动其他线程。那些其他线程将请求完成作业,而主线程将使其他线程可以查看和执行作业。
必须完成的工作是将巨大的布尔数组中的索引设置为真。它们默认为 false,其他线程只能将它们设置为 true,而不能设置为 false。各种作业可能涉及将相同的索引设置为 true。
主线程根据两件事找到新工作。
- 巨大的布尔数组中的值。
- 哪些工作已经完成。
如何确保主线程从巨大的布尔数组中读取新值?
我不能通过同步方法更新数组,因为几乎所有其他线程都这样做,因此我只能获得相当多的顺序性能。
假设其他线程通过非同步函数将其许多索引设置为 true 来更新巨大的布尔数组。我怎样才能确保主线程读取更新并确保它不只是本地缓存在线程中?有什么方法可以让它 "push" 更新吗?我猜主线程应该只使用同步方法来 "get" 更新?
我会使用另一种设计模式。例如,您可以将布尔值的索引添加到 Set 中,例如,当它们打开时,然后同步对其的访问。然后就可以用wait/notify唤醒了
首先,一般不要使用布尔数组,使用BitSets。看到这个:boolean[] vs. BitSet: Which is more efficient?
在这种情况下,您需要一个原子位集,因此您不能使用 java.util.BitSet,但这里有一个:AtomicBitSet implementation for java
要真正完整地回答您的问题,您应该打开一份 Java 语言规范,然后搜索“发生在之前”。
当 JLS 说 A“发生在”B 之前时,这意味着在 Java 语言的有效实现中,A 需要实际发生在 B 之前。规范说的是:
如果某个线程更新了一个字段,然后释放了一个锁(例如,
留下一个同步块),更新“发生在”锁定之前
已发布,
如果某个线程释放了锁,而其他线程随后释放了锁
获取相同的锁,释放“发生在”获取之前。
如果某个线程获取了一个锁,然后读取一个字段,则
收购“发生在”阅读之前。
由于“happens before”是一个传递关系,你可以推断如果线程A更新了一个同步块中的一些变量,然后线程B检查了一个在同一个对象上同步的块中的变量,那么线程B会看到A写了什么帖子
除了进入和离开同步块,还有很多其他事件(构造对象、wait()ing/notify()ing 对象、start()ing 和 join()ing 线程、读写 volatile变量)允许您在线程之间建立“发生在之前”的关系。
虽然不能快速解决您的问题,但本章值得一读。
...the main thread will make jobs available for the other threads to see and do...
I can't have the update of the array be through a synchronized method, because that's pretty much all the other threads do, and ...
听起来你是在说每个工作线程在必须等待来自 main() 线程的进一步指令之前只能做一些微不足道的工作。如果这是真的,那么大多数工人将大部分时间都在等待。如果您只在一个线程中完成所有工作,您可能会获得更好的性能。
假设您的目标是充分利用多处理器机器的可用周期,您将需要以某种方式划分工作,让每个工作线程关闭并在需要之前完成大部分工作与任何其他线程同步。
您可以改为将其建模为消息传递而不是改变共享状态。在您的描述中,工作人员从不读取布尔数组,只写入完成状态。您是否考虑过使用工作人员从中消费的待处理作业队列和主人读取的完成队列?主线程可以有效地维护作业状态字段,而无需担心任何共享状态。根据您的需要,您可以使用阻塞或非阻塞队列。
我有一个主线程将启动其他线程。那些其他线程将请求完成作业,而主线程将使其他线程可以查看和执行作业。
必须完成的工作是将巨大的布尔数组中的索引设置为真。它们默认为 false,其他线程只能将它们设置为 true,而不能设置为 false。各种作业可能涉及将相同的索引设置为 true。
主线程根据两件事找到新工作。
- 巨大的布尔数组中的值。
- 哪些工作已经完成。
如何确保主线程从巨大的布尔数组中读取新值?
我不能通过同步方法更新数组,因为几乎所有其他线程都这样做,因此我只能获得相当多的顺序性能。
假设其他线程通过非同步函数将其许多索引设置为 true 来更新巨大的布尔数组。我怎样才能确保主线程读取更新并确保它不只是本地缓存在线程中?有什么方法可以让它 "push" 更新吗?我猜主线程应该只使用同步方法来 "get" 更新?
我会使用另一种设计模式。例如,您可以将布尔值的索引添加到 Set 中,例如,当它们打开时,然后同步对其的访问。然后就可以用wait/notify唤醒了
首先,一般不要使用布尔数组,使用BitSets。看到这个:boolean[] vs. BitSet: Which is more efficient?
在这种情况下,您需要一个原子位集,因此您不能使用 java.util.BitSet,但这里有一个:AtomicBitSet implementation for java
要真正完整地回答您的问题,您应该打开一份 Java 语言规范,然后搜索“发生在之前”。
当 JLS 说 A“发生在”B 之前时,这意味着在 Java 语言的有效实现中,A 需要实际发生在 B 之前。规范说的是:
如果某个线程更新了一个字段,然后释放了一个锁(例如, 留下一个同步块),更新“发生在”锁定之前 已发布,
如果某个线程释放了锁,而其他线程随后释放了锁
获取相同的锁,释放“发生在”获取之前。如果某个线程获取了一个锁,然后读取一个字段,则 收购“发生在”阅读之前。
由于“happens before”是一个传递关系,你可以推断如果线程A更新了一个同步块中的一些变量,然后线程B检查了一个在同一个对象上同步的块中的变量,那么线程B会看到A写了什么帖子
除了进入和离开同步块,还有很多其他事件(构造对象、wait()ing/notify()ing 对象、start()ing 和 join()ing 线程、读写 volatile变量)允许您在线程之间建立“发生在之前”的关系。
虽然不能快速解决您的问题,但本章值得一读。
...the main thread will make jobs available for the other threads to see and do...
I can't have the update of the array be through a synchronized method, because that's pretty much all the other threads do, and ...
听起来你是在说每个工作线程在必须等待来自 main() 线程的进一步指令之前只能做一些微不足道的工作。如果这是真的,那么大多数工人将大部分时间都在等待。如果您只在一个线程中完成所有工作,您可能会获得更好的性能。
假设您的目标是充分利用多处理器机器的可用周期,您将需要以某种方式划分工作,让每个工作线程关闭并在需要之前完成大部分工作与任何其他线程同步。
您可以改为将其建模为消息传递而不是改变共享状态。在您的描述中,工作人员从不读取布尔数组,只写入完成状态。您是否考虑过使用工作人员从中消费的待处理作业队列和主人读取的完成队列?主线程可以有效地维护作业状态字段,而无需担心任何共享状态。根据您的需要,您可以使用阻塞或非阻塞队列。