并发线程安全的 AtomicInteger

Posted

技术标签:

【中文标题】并发线程安全的 AtomicInteger【英文标题】:Concurrent Thread-safe AtomicInteger 【发布时间】:2015-03-20 12:08:51 【问题描述】:

我已经阅读了 java.util.concurrent 包的 API 文档,但显然误解了一些东西。概述说

一个支持无锁线程安全的类的小工具包 对单个变量进行编程。

但是,一个小型测试应用程序表明 AtomicInteger 类不提供线程安全性,至少在跨线程共享时(我接受 getAndSet / increment 方法本身至少是原子 )

测试:

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntTest

    public static void main(String[] args) throws InterruptedException
    
        AtomicInteger atomicInt = new AtomicInteger(0);
        WorkerThread w1 = new WorkerThread(atomicInt);
        WorkerThread w2 = new WorkerThread(atomicInt);
        w1.start();
        w2.start();
        w2.join(); // <-- As pointed out by StuartLC and BarrySW19, this should be w1.join(). This typo allows the program to produce variable results because it does not correctly wait for *both* threads to finish before outputting a result.
        w2.join();
        System.out.println("Final value: " + atomicInt.get());
    

    public static class WorkerThread extends Thread
    
        private AtomicInteger atomicInt = null;
        private Random random = new Random();

        public WorkerThread(AtomicInteger atomicInt)
        
            this.atomicInt = atomicInt;
        

        @Override
        public void run()
        
            for (int i = 0; i < 500; i++)
            
                this.atomicInt.incrementAndGet();
                try
                
                    Thread.sleep(this.random.nextInt(50));
                
                catch(InterruptedException e)
                
                    e.printStackTrace();
                
            
        
    

当我运行这个课程时,我总是得到大约 950 到 1000 的结果,而我希望总是看到正好 1000。

你能解释一下为什么当两个线程访问这个共享的 AtomicInteger 变量时我不能得到一致的结果吗?我是否误解了线程安全保证?

【问题讨论】:

您有两次w2.join() - 一次应该是w1.join(),也就是说,您不必在打印前等待w1 完成。 P.S.:“支持线程安全编程”与“保证线程安全”并不相同。即使程序中的每个对象都是“线程安全的”,这也不会使程序本身成为“线程安全的”。如果您还没有完成 Oracle“并发”教程,您应该完成:docs.oracle.com/javase/tutorial/essential/concurrency/… 特别注意讨论可能出错的部分以及如何避免这些部分。 嘿嘿嘿,愚蠢的错字几乎颠覆了我的世界观,谢谢! 【参考方案1】:

看起来像一个简单的剪切和粘贴错误 - 您加入线程“w2”两次,但从未加入“w1”。目前,当您打印 'final' 值时,您会期望线程“w1”仍在运行一半的时间。

【讨论】:

以上是关于并发线程安全的 AtomicInteger的主要内容,如果未能解决你的问题,请参考以下文章

并发遍历实现线程安全遍历

Go Map 为啥是非线程安全的?

并发容器线程安全应对之道-ConcurrentHashMap

高并发学习 —— 集合线程安全线程池

Java并发编程——常见的线程安全问题

Java 并发 – 线程安全?