Java原子操作
示例
原子操作是“一次全部”执行的操作,在原子操作的执行过程中没有其他线程观察或修改状态的机会。
让我们考虑一个糟糕的例子。
private static int t = 0; public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(400); //高线程数是出于演示目的。 for (int i = 0; i < 100; i++) { executorService.execute(() -> { t++; System.out.println(MessageFormat.format("t: {0}", t)); }); } executorService.shutdown(); }
在这种情况下,有两个问题。第一个问题是后递增运算符不是原子的。它包含多个操作:获取值,将值加1,设置值。这就是为什么如果运行示例,很可能t:100在输出中看不到-两个线程可以同时获取值,对其进行递增和设置:假设t的值为10,并且两个线程在递增t。两个线程都将t的值设置为11,因为第二个线程在第一个线程完成对t的递增之前观察到t的值。
第二个问题是我们如何观测t。当我们打印t的值时,该线程的增量操作后,该值可能已经被另一个线程更改了。
为了解决这些问题,我们将使用java.util.concurrent.atomic.AtomicInteger,其中包含许多原子操作供我们使用。
private static AtomicInteger t = new AtomicInteger(0); public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(400); //高线程数是出于演示目的。 for (int i = 0; i < 100; i++) { executorService.execute(() -> { int currentT = t.incrementAndGet(); System.out.println(MessageFormat.format("t: {0}", currentT)); }); } executorService.shutdown(); }
该incrementAndGet方法以AtomicInteger原子方式递增并返回新值,从而消除了先前的竞争条件。请注意,在此示例中,线路仍会乱序,因为我们不努力对println调用进行排序,并且这超出了此示例的范围,因为它需要同步,并且此示例的目标是显示如何用于AtomicInteger消除与状态有关的比赛条件。