前言
只有光头才能变强
之前已经写过多线程相关的文章了,有兴趣的同学可以去了解一下:
在阅读《阿里巴巴 Java开发手册》读后感时,还有未解决的问题:
如果是count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1);如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。
之前在学习的时候也看过AtomicInteger类很多次了,一直没有去做相关的笔记。现在遇到问题了,于是就过来写写笔记,并希望在学习的过程中解决掉问题。
一、基础铺垫
首先我们来个例子:
 public class AtomicMain {      public static void main(String[] args) throws InterruptedException {          ExecutorService service = Executors.newCachedThreadPool();          Count count = new Count();         // 100个线程对共享变量进行加1         for (int i = 0; i < 100; i++) {             service.execute(() -> count.increase());         }          // 等待上述的线程执行完         service.shutdown();         service.awaitTermination(1, TimeUnit.DAYS);           System.out.println("公众号:Java3y---------");         System.out.println(count.getCount());     }  }  class Count{      // 共享变量     private Integer count = 0;     public Integer getCount() {         return count;     }     public  void increase() {         count++;     } }你们猜猜得出的结果是多少?是100吗?
多运行几次可以发现:结果是不确定的,可能是95,也可能是98,也可能是100
根据结果我们得知:上面的代码是线程不安全的!如果线程安全的代码,多次执行的结果是一致的!
我们可以发现问题所在:count++并不是原子操作。因为count++需要经过读取-修改-写入三个步骤。举个例子:
- 如果某一个时刻:线程A读到count的值是10,线程B读到count的值也是10
- 线程A对count++,此时count的值为11
- 线程B对count++,此时count的值也是11(因为线程B读到的count是10)
- 所以到这里应该知道为啥我们的结果是不确定了吧。
要将上面的代码变成线程安全的(每次得出的结果是100),那也很简单,毕竟我们是学过synchronized锁的人:
- 在increase()加synchronized锁就好了
 public synchronized void increase() {     count++; }无论执行多少次,得出的都是100:
                        
