通过线程分片资源?

时间:2019-06-13 17:52:28

标签: java multithreading

我有一个(有限的)线程池,该池执行CPU绑定的任务。我想在一个地方汇总来自每个线程的一些数字统计信息。基本上:每个线程都会以非常高的频率更新某些共享的统计信息(例如,其工作花费了多长时间),而在更慢的时间间隔内,“统计信息读取器”会查询这些统计信息。

我的第一个想法是使用一些共享原子并从每个线程更新它们。这行得通,但是在我的测试中,由于存在很多争用,原子的开销可能会变得很高,所以我试图考虑其他一些选择。

我的第二个问题是一种“分片”方案,其中每个线程都有自己的stats对象,可以在不进行任何同步的情况下对其进行更新。然后,“状态读取器”可以将每个线程的状态汇总为一个总体状态值。

我的第一个问题是:线程分片方案有意义吗?是否存在我正在重新发明的东西?

我的第二个问题是:如果分片方案确实有意义,我正在尝试考虑将线程映射到其分片的最佳方法:

1)使用线程的ID mod一些分片值来获取分片索引,但是我不认为这是可靠的,因为我认为线程ID值是共享的,因此可能会发生冲突。

2)向线程添加线程本地索引,但是我认为ExecutorService不能很好地发挥作用。

3)我可以继承Thread的子类,但是当我想访问它时,如果可能的话,我必须强制转换它。

4)创建线程后,创建其名称到其碎片的映射。可以,但是在创建线程时会出现竞争:我们可能会在向地图添加新的分片的同时查找其分片,从而导致并发问题。

想知道我是不是在这里偏离了基础,并对此进行了过度思考(似乎是一个常见问题?),或者这些方案之一对用例是否有意义。

2 个答案:

答案 0 :(得分:0)

我建议您使用其他方式:Actor

  

参与者模型提供了一个相对简单但功能强大的模型,用于设计和实现应用程序,这些应用程序可以在所有系统资源(从线程和核心到服务器和数据中心的集群)中分配和共享工作。它为构建具有高并发级别的应用程序和提高资源效率级别提供了有效的框架。重要的是,参与者模型还具有定义良好的方式来优雅地处理错误和故障,从而确保一定程度的弹性,可以隔离问题并防止级联故障和大量停机。

我认为您可以转向Akka

答案 1 :(得分:0)

解决此问题的一种方法是使用LongAdder类,该类避免了普通旧原子遭受的争用。

一种更手写的方法是创建一个类,该类保存要为每个线程收集的统计信息,然后具有这些对象的数组,以使每个线程的stats对象位于array[thread.getId() % NUM_THREADS]中。然后,读取器线程可以遍历数组并根据需要收集统计信息。

使此命令有效运行的诀窍是避免使用false sharing。也就是说,不同内核上的线程会对其各自的对象执行更新,但是这些对象恰好位于同一条缓存行上,从而导致大量不必要的缓存一致性流量。

在Java 8中,您可能需要研究@Contended注释。用一堆long字段填充类的旧方法不再起作用,因为未使用的字段将被优化。