还在用Synchronized?Atomic你了解不?

 

前言

只有光头才能变强

之前已经写过多线程相关的文章了,有兴趣的同学可以去了解一下:

多线程文章

在阅读《阿里巴巴 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:

关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信