java并发:多个写操作者,一个读操作者
java并发:多个写操作者,一个读操作者
我需要在我的软件中收集一些统计数据,并且我试图使其快速和正确,这对我来说并不容易!\n首先是我目前的代码,有两个类,StatsService和StatsHarvester。\npublic class StatsService {\n private Map
\n
编辑
\n感谢大家详细而有帮助的答案。正如我预料的那样,有不止一种方法可以解决这个问题。\n我测试了你们大部分提议(我理解的那些),并上传了一个测试项目到谷歌代码以供参考(maven项目)。\n我测试了不同实现的StatsService:\n
- \n
- HashMapStatsService (HMSS)
- ConcurrentHashMapStatsService (CHMSS)
- LinkedQueueStatsService (LQSS)
- GoogleStatsService (GSS)
- ExecutorConcurrentHashMapStatsService (ECHMSS)
- ExecutorHashMapStatsService (EHMSS)
\n
\n
\n
\n
\n
\n
\n并且我用每个调用notify y
次的x
个线程测试了它们,结果以毫秒为单位:\n
10,100 10,1000 10,5000 50,100 50,1000 50,5000 100,100 100,1000 100,5000 GSS 1 5 17 7 21 117 7 37 254 Summe: 466 ECHMSS 1 6 21 5 32 132 8 54 249 Summe: 508 HMSS 1 8 45 8 52 233 11 103 449 Summe: 910 EHMSS 1 5 24 7 31 113 8 67 235 Summe: 491 CHMSS 1 2 9 3 11 40 7 26 72 Summe: 171 LQSS 0 3 11 3 16 56 6 27 144 Summe: 266
\n目前我认为我将使用ConcurrentHashMap,因为它提供了良好的性能,同时也很容易理解。\n感谢大家的意见!\nJanning
在Java中,当有多个写入者和一个读取者时,可能会出现并发性问题。这种情况下,多个写入者同时写入数据,而读取者在读取数据时可能会读到不一致的结果。为了解决这个问题,可以使用Java的util.concurrent库。
首先,可以使用ConcurrentLinkedQueue来实现该解决方案。每个写入者可以自由地向这个队列中写入数据,而不需要担心其他写入者。它可以将一个带有统计数据的对象放入队列中。
接下来,读取者可以持续地从队列中取出数据并进行处理。然后,它可以按照自己的需求进行存储。
使用ConcurrentLinkedQueue可以确保多个写入者之间的并发写入没有问题,并且读取者可以按照自己的节奏读取数据进行处理。
下面是使用ConcurrentLinkedQueue的示例代码:
import java.util.concurrent.ConcurrentLinkedQueue; public class Example { private static ConcurrentLinkedQueue
使用ConcurrentLinkedQueue可以有效地解决多个写入者和一个读取者之间的并发性问题,保证数据的一致性和正确性。
Java并发:多个写者,一个读者
在并发编程中,一个常见的问题是多个写者和一个读者的情况。多个写者同时修改共享数据可能导致数据不一致的问题,而一个读者同时读取共享数据不会引发问题。为了解决这个问题,可以使用java.util.concurrent.ConcurrentHashMap来处理并发访问,避免对整个Map进行无用的锁定,并节省大量的工作。
ConcurrentHashMap是一个支持全面并发的哈希表,它支持可调整的更新操作的预期并发性。尽管所有操作都是线程安全的,但检索操作不需要锁定,并且没有任何支持可以阻止所有访问的锁定整个表的机制。可以通过指定并发级别来设置其并发级别,以指导更新操作之间的允许并发性。理想情况下,您应该选择一个值来容纳将同时修改表的线程数量。过高或过低的值都会导致问题,但是数量估计在一个数量级内的高估和低估通常不会有太大的影响。当只有一个线程修改且所有其他线程只读时,值为1是适当的。
要确保原子性,可以使用ConcurrentMap接口中的putIfAbsent、replace和remove等操作。然而,这种方法很容易导致丢失的更新。根据Map的契约,它不应该允许丢失的更新,可以假设putIfAbsent是原子执行的。为了确保这一点,需要使用循环直到replace操作返回true。另外,可以考虑将AtomicLong或AtomicInteger作为Map的值,这将消除丢失增量的问题。
对于多个写者和一个读者的情况,可以使用ConcurrentHashMap来解决并发访问的问题。通过选择适当的并发级别和使用原子操作,可以确保数据的一致性和原子性。同时,使用AtomicLong或AtomicInteger作为Map的值可以消除丢失增量的问题。
Java并发:多个写者,一个读者
在多个写者和一个读者的情况下,可能会出现并发问题,需要解决并发问题以确保线程安全。为了解决这个问题,可以使用java.util.concurrent库中的ConcurrentHashMap和AtomicLong。
这里的代码使用了ConcurrentHashMap和AtomicLong来解决并发问题。首先,通过stats.get(key)方法获取key对应的AtomicLong对象,如果返回值为null,说明该key在ConcurrentHashMap中不存在,需要使用putIfAbsent(key, new AtomicLong(1))方法将其放入ConcurrentHashMap中。如果返回值不为null,则直接调用value.incrementAndGet()方法对AtomicLong对象进行增加操作。
这种解决方法既快速又线程安全。通过使用ConcurrentHashMap和AtomicLong,可以避免并发问题的发生。
需要注意的是,原始的代码中使用了putIfAbsent方法进行判断,但这并不是ConcurrentHashMap的正确使用方法。每次调用putIfAbsent都会锁定与hashCode相关联的单个条目,从而降低了性能。正确的做法是,在初始的get方法成功后,不再进行putIfAbsent操作,避免不必要的锁定。
除了上述解决方法,还要注意第二个方法getStats存在的问题,即复制和清除操作不是原子性的。针对这个问题,可以采取其他的解决方法。
,通过使用ConcurrentHashMap和AtomicLong,可以解决多个写者和一个读者的并发问题。但需要注意正确使用ConcurrentHashMap的putIfAbsent方法,避免不必要的锁定。另外,对于存在的其他问题,需要采取其他解决方法。