gpt4 book ai didi

多个变量之间的java线程安全

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:57:11 26 4
gpt4 key购买 nike

我有一个 Metrics 类,它应该跟踪我们每秒处理多少事务以及它们需要多长时间。其结构的相关部分如下所示:

public class Metrics {
AtomicLong sent = new AtomicLong();
AtomicLong totalElapsedMsgTime = new AtomicLong();

AtomicLong sentLastSecond = new AtomicLong();
AtomicLong avgTimeLastSecond = new AtomicLong();

public void outTick(long elapsedMsgTime){
sent.getAndIncrement();
totalElapsedMsgTime.getAndAdd(elapsedMsgTime);
}

class CalcMetrics extends TimerTask {
@Override
public void run() {
sentLastSecond.set(sent.getAndSet(0));
long tmpElapsed = totalElapsedMsgTime.getAndSet(0);
long tmpSent = sentLastSecond.longValue();

if(tmpSent != 0) {
avgTimeLastSecond.set(tmpElapsed / tmpSent);
} else {
avgTimeLastSecond.set(0);
}
}
}
}

我的问题是 outTick 函数每秒会被许多不同的线程调用数百次。作为 AtomicLong 已经确保每个变量都是 单独 线程安全的,并且它们在该函数中不会相互交互,所以我不想要一个会调用 outTick 的锁阻塞另一个线程的调用 outTick。如果几个不同的线程递增 sent 变量然后它们都添加到 totalElapsedMsgTime 变量,那就完全没问题了。

但是,一旦它进入 CalcMetrics run 方法(每秒只发生一次),它们就会相互作用。我想确保我可以获取并重置这两个变量,而不会处于 outTick 调用的中间,或者在获取一个变量和下一个变量之间发生另一个 outTick 调用。

有什么办法吗? (我的解释有道理吗?)有没有一种方法可以说 A 不能与 B 交错,但多个 B 可以相互交错?


编辑:

我选择了 James 建议的 ReadWriteLock。对于任何感兴趣的人,这是我的结果:

public class Metrics {
AtomicLong numSent = new AtomicLong();
AtomicLong totalElapsedMsgTime = new AtomicLong();

long sentLastSecond = 0;
long avgTimeLastSecond = 0;

private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();

public void outTick(long elapsedMsgTime) {
readLock.lock();
try {
numSent.getAndIncrement();
totalElapsedMsgTime.getAndAdd(elapsedMsgTime);
}
finally
{
readLock.unlock();
}
}

class CalcMetrics extends TimerTask {

@Override
public void run() {
long elapsed;

writeLock.lock();
try {
sentLastSecond = numSent.getAndSet(0);
elapsed = totalElapsedMsgTime.getAndSet(0);
}
finally {
writeLock.unlock();
}

if(sentLastSecond != 0) {
avgTimeLastSecond = (elapsed / sentLastSecond);
} else {
avgTimeLastSecond = 0;
}
}
}
}

最佳答案

通常的解决方案是将所有变量包装为一种原子数据类型。

class Data
{
long v1, v2;

Data add(Data another){ ... }
}

AtomicReference<Data> aData = ...;

public void outTick(long elapsedMsgTime)
{
Data delta = new Data(1, elapsedMsgTime);

aData.accumulateAndGet( delta, Data:add );
}

在您的情况下,它可能不会比仅仅锁定快多少。

java8 中还有另一个有趣的锁 - StampedLock . javadoc 示例与您的用例非常匹配。基本上,您可以对多个变量进行乐观读取;之后,检查以确保在读取期间没有进行任何写入。在您的情况下,每秒“数百”次写入,乐观读取大多会成功。

关于多个变量之间的java线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33247828/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com