繁体   English   中英

线程池,共享数据,Java同步

[英]Thread Pool, Shared Data, Java Synchronization

说,我有一个数据对象:

class ValueRef { double value; }

每个数据对象存储在主集合中的位置:

Collection<ValueRef> masterList = ...;

我还有一组作业,每个作业都有一个本地的数据对象集合(每个数据对象也出现在masterList ):

class Job implements Runnable { 
     Collection<ValueRef> neededValues = ...; 
     void run() {
         double sum = 0;
         for (ValueRef x: neededValues) sum += x;
         System.out.println(sum);
     } 
}

用例:

  1. for (ValueRef x: masterList) { x.value = Math.random(); }

  2. 使用某些作业填充作业队列。

  3. 唤醒线程池

  4. 等到每个工作都被评估

注意:在作业评估期间,所有值都是常量。 但是,线程可能已经评估过去的作业,并保留缓存的值。

问题:确保每个线程看到最新值所需的最小同步量是多少?

我理解从monitor / lock-perspective进行同步,我不理解从cache / flush-perspective进行同步(即,在同步块的进入/退出时内存模型保证了什么)。

对我来说,感觉我需要在更新值的线程中同步一次以将新值提交到主内存,并且每个工作线程一次,以刷新缓存以便读取新值。 但我不确定如何最好地做到这一点。

我的方法:创建一个全局监视器: static Object guard = new Object(); 然后,在更新主列表时同步guard 最后,在启动线程池之前,对池中的每个线程执行一次,在空块中同步guard

这真的会导致该线程读取的任何值的完全刷新吗? 或者只是在同步块内触及的值? 在这种情况下,可能我应该在循环中读取每个值一次,而不是空块?

谢谢你的时间。


编辑:我认为我的问题可归结为,一旦我退出同步块,每次第一次读取(在该点之后)都会进入主存储器吗? 无论我同步什么?

线程池的线程在过去评估过某些作业并不重要。

Executor Javadoc说:

内存一致性效果:在将Runnable对象提交给Executor之前,线程中的操作发生在执行开始之前,可能在另一个线程中。

因此,只要您在提交作业之前使用标准线程池实现并更改数据,就不必担心内存可见性效果。

你的计划听起来足够了。 这取决于你打算如何“唤醒线程池”。

Java内存模型规定,在进入synchronized块之前由线程执行的所有写操作对于随后在该锁上同步的线程是可见的。

因此,如果在更新主列表期间确定工作线程在wait()调用中被阻塞(必须在synchronized块内),则当它们唤醒并变为可运行时,主线程所做的修改将对这些线程可见。

但是,我鼓励您在java.util.concurrent包中应用更高级别的并发实用程序。 这些将比您自己的解决方案更强大,并且是在深入研究之前学习并发性的好地方。


只是为了澄清:在不使用同步块的情况下控制工作线程几乎是不可能的,其中进行检查以查看工作者是否有任务要执行。 因此,在工作线程唤醒之前,控制器线程对作业所做的任何更改都会发生。 您需要一个synchronized块,或至少一个volatile变量作为内存屏障; 但是,我无法想象你如何使用其中一个创建一个线程池。

作为使用java.util.concurrency包的优点的一个例子,请考虑这一点:您可以使用带有wait()调用的synchronized块, 带有volatile变量的busy-wait循环。 由于线程之间上下文切换的开销,繁忙的等待实际上可以在某些条件下更好地执行 - 没有必要乍看之下可能假设的可怕想法。

如果您使用并发实用程序(在这种情况下,可能是ExecutorService ),可以为您做出最佳选择,包括环境,任务的性质以及在给定时间内其他线程的需求。 自己实现这种优化水平是很多不必要的工作。

为什么不将Collection<ValueRef>ValueRef不可变,或者在发布对集合的引用后至少不修改集合中的值。 那你就不用担心同步了。

这是您想要更改集合的值,创建新集合并在其中添加新值。 设置值后,通过集合引用新的作业对象。

不这样做的唯一原因是,如果集合的大小太大以至于它几乎不适合内存并且您无法承受两份副本,或者集合的交换会导致垃圾收集器的工作量太大(证明在为线程代码使用可变数据结构之前,其中一个是一个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM