【问题标题】:AtomicLong in Multithreading多线程中的 AtomicLong
【发布时间】:2020-04-04 17:44:54
【问题描述】:

我正在尝试在多线程环境中使用 AtomicLong。我想要的结果不起作用,

public class Account implements Runnable {

    private final AtomicLong amount = new AtomicLong(0);

    public Account(long difference) {
        amount.set(difference);
    }

    public void run() {
        System.out.println("The Balance is : " + amount);
    }
}
public class Examples {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        IntStream.range(0, 100).forEach(i -> service.submit(new Account(i)));
    }
}

我尝试使用读/写锁和块级同步,但没有成功。请问有人发现哪里出错了吗?

I 输出必须从 0,1,2...99 开始。

【问题讨论】:

  • 您希望它们按顺序打印吗?在单个线程中执行它们(或使用 common 计数器并正确同步访问)。
  • 谢谢汤姆。让我尝试共享数据并使其在多线程中工作。
  • 我会注意到,如果您使用单个条件(内部锁的锁),如果您有大量活动线程,性能将会很差,因为它会唤醒所有线程数字 - O(n^2)。
  • 是的,我同意使用内在锁的性能。
  • 另外,如果你最终得到大量线程,那么你需要重新考虑代码。同步是您的最后一个问题

标签: java multithreading java.util.concurrent


【解决方案1】:

您的任何线程之间没有共享数据...每个线程都创建自己的AtomicLong,没有共享。即使它是共享的,也不能保证这些数字会按顺序打印。它们甚至不能保证打印所有数字(其中一些可以打印多次)。

如果我这样更改您的代码:

public static class Account implements Runnable {
    private static final AtomicLong amount = new AtomicLong(0);

    public Account(long difference) {
        amount.set(difference);
    }

    public void run() {
        System.out.println("The Balance is : " + amount);
    }
}

这是我得到的输出示例:

The Balance is : 2
The Balance is : 2
The Balance is : 2
The Balance is : 4
The Balance is : 5
The Balance is : 6
The Balance is : 7
The Balance is : 8
The Balance is : 9
The Balance is : 9

原因是以下情况是可能的:

  • 主线程创建Account(0)。这会将0 写入共享的AtomicLong。,
  • Thread-0 启动
  • 主线程创建Account(1)。这会将1 写入共享的AtomicLong
  • Thread-0 打印值,现在是 1。在这种情况下,0 将永远不会被打印出来。

AtomicLong 唯一能向您保证的是,所有对该值的写入将始终被之后进行读取的所有线程可见。

javadoc 中详细介绍了原子变量的属性(请参阅here)。

【讨论】:

  • “甚至不能保证所有数字都会被打印(有些可能会打印两次)” 我不明白这个说法:为什么不呢? (/为什么?)为范围内的每个 i 创建一个类的实例。
  • 我误读并认为对set 的调用和对get 的调用不是原子的,因为它们位于两个单独的方法(构造函数和run 方法)中。但实际上,所有实例都会在主线程中按顺序创建,只有读取以多线程方式发生,所以是的,它们都将被打印,没有顺序保证。谢谢,将修复:p
  • 感谢@Dici 和 Andy 的解释
  • @AndyTurner 好的,所以我将回复您的评论,但会让答案更清楚。我的意思是如果数据是共享的,我们不能保证所有的数字都被打印出来,我只是确认了这一点。原因是这样的场景是可能的:1)主线程创建Account(0),2)Thread-0启动3)主线程创建Account(1)(将1写入共享AtomicLong),4)@ 987654344@ 打印值,现在是1。在这种情况下,0 将永远不会被打印
【解决方案2】:

也许你的意思是这样的:

public class Account implements Runnable {

    private final static AtomicLong seq = new AtomicLong(0);
    private final long amount;

    public Account() {
        amount = seq.getAndIncrement();
    }

    public void run() {
        System.out.println("The Balance is : " + amount);
    }
}

public class Examples {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        IntStream.range(0, 100).forEach(i -> service.submit(new Account()));
    }
}

这将保证每个帐户都将获得一个唯一的值amount,但这些值可以以不同的顺序打印。

【讨论】:

  • 正确,我也尝试过这种方法,得到的顺序不同。
  • @parrotjack 好吧,如果您确实需要保留确切的顺序,那么使用多线程是没有意义的。您想要的是串行执行 - 这可以通过单线程执行直接保证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
相关资源
最近更新 更多