如何确保AtomicLong addAndGet结果的正确性

时间:2018-06-01 06:33:31

标签: java thread-safety atomic-long

我想在我的多线程下载程序中计算当前百分比。但是有一个奇怪的问题。 第二次下载期间的lastDownloadSize必须是lastDown的write和lastDownloadSize的总和。 example

有我的代码

private long getDownloadSize() {
    synchronized (this) {
        final AtomicLong totalWriteCount = new AtomicLong(0);
        final AtomicLong lastDownloadSize = new AtomicLong(0);
        for (DownloadTask task : downloadTasks) {
            final long writeCount = task.getWriteCount();
            totalWriteCount.addAndGet(writeCount);
            final long downloadSize = task.getPosition().getDownloadSize();
            lastDownloadSize.addAndGet(downloadSize);
        }
        System.out.println("=====  writeCount : " + totalWriteCount + "lastDownloadSize : " + lastDownloadSize);
        return totalWriteCount.addAndGet(lastDownloadSize.get());
    }
}

2 个答案:

答案 0 :(得分:1)

您的totalWriteCountlastDownloadSize变量是getDownloadSize()方法的局部变量。在这种情况下,使用AtomicLong没有意义,因为只有一个线程可以访问它们。

您可能想要的是让您的班级成员totalWriteCountlastDownloadSize成员:

class SomeClass {
    // ...
    final AtomicLong totalWriteCount = new AtomicLong(0);
    final AtomicLong lastDownloadSize = new AtomicLong(0);
    // ...

    private long getDownloadSize() {
        synchronized (this) {
            for (DownloadTask task : downloadTasks) {
                final long writeCount = task.getWriteCount();
                totalWriteCount.addAndGet(writeCount);
                final long downloadSize = task.getPosition().getDownloadSize();
                lastDownloadSize.addAndGet(downloadSize);
            }
            System.out.println("=====  writeCount : " + totalWriteCount + "lastDownloadSize : " + lastDownloadSize);
            return totalWriteCount.addAndGet(lastDownloadSize.get());
        }
    }
}

但是,在这种情况下,如果仅从synchronized(this)块中访问它们,则不需要使用AtomicLong,因为synchronized块已经确保它们仅被访问一个单独的线程同时。

答案 1 :(得分:1)

您当前的设置无效,因为您以错误的方式使用AtomicLong。在单个线程中定义任何Atomic - 类只是对该API的误用。

现在为什么我说单线程,当你有人输入你的方法时你正在进行同步,这只是说一次只有一个线程可以使用所述方法。这引出了我们的问题:

  • AtomicLong是一个本地变量

您可能希望将downloadSizetotalWriteCount定义为class成员。 E.g:

public class YourClass {
    private final AtomicLong totalWriteCount = new AtomicLong(0);        
    private final AtomicLong downloadSize = new AtomicLong(0);

    /* constructors and other methods */

    private synchronized long getDownloadSize() {
        for (DownloadTask task : downloadTasks) {
            final long writeCount = task.getWriteCount();
            totalWriteCount.addAndGet(writeCount);
            final long downloadSize = task.getPosition().getDownloadSize();
            lastDownloadSize.addAndGet(downloadSize);
        }
        System.out.println("=====  writeCount : " + totalWriteCount + "lastDownloadSize : " + lastDownloadSize);
        return totalWriteCount.addAndGet(lastDownloadSize.get());
    }
}