Java Atomic类及线程同步新机制原理解析

一、为什么要使用Atomic类?

看一下下面这个小程序,模拟计数,创建10个线程,共同访问这个int count = 0 ;每个线程给count往上加10000,这个时候你需要加锁,如果不加锁会出现线程安全问题,但是使用AtomicInteger之后就不用再做加锁的操作了,因为AtomicInteger内部使用了CAS操作,直接无锁往上递增,有人会问问什么会出现无锁操作,答案只有一个:那就是快呗;

下面是AtomicInteger的使用方法:

package com.example.demo.threaddemo.juc_008;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author D-L
 * @Classname T01_AtomicInteger
 * @Version 1.0
 * @Description 使用AtomicInteger类代替synchronized
 * @Date 2020/7/22
 */
public class T01_AtomicInteger {
//  int count = 0;
   AtomicInteger count = new AtomicInteger(0);

  public /**synchronized*/ void m(){
    for (int i = 0; i < 10000; i++) {
//      count++;
      count.incrementAndGet();
    }
  }

  public static void main(String[] args) {
    T01_AtomicInteger t = new T01_AtomicInteger();
    List<Thread> threads = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
      threads.add(new Thread(t::m ,"Thread" + i));
    }
    threads.forEach(o -> o.start());
    threads.forEach(o ->{
      try {
        o.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    /* for (int i = 0; i < 10; i++) {
      new Thread(t::m ,"Thread"+i).start();
    }
    try {
      TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }*/
    System.out.println(t.count);
  }
}

二、Atomic类,synchronized、LongAdder的效率验证 及 分析

模拟多个线程对一个数进行递增,多线程对一个共享变量进行递增的方法大概有三种;验证一下它们的效率,这里做一些粗糙的测试,基本已经能说明问题,具体情况还要根据实际情况:

第一种:使用long count = 0; 加锁来实现;

第二种:使用AtomicLong类来实现;

第三种:使用LongAdder实现;

package com.example.demo.threaddemo.juc_008;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

/**
 * @author D-L
 * @Classname T02_AtomicVsSyncVsLongAdder
 * @Version 1.0
 * @Description 测试Atomic类 synchronized LongAdder效率
 * @Date 2020/7/22
 */
public class T02_AtomicVsSyncVsLongAdder {
  static AtomicLong count1 = new AtomicLong(0L);
  static Long count2 = 0L;
  static LongAdder count3 = new LongAdder();

  public static void main(String[] args) throws InterruptedException {
    Thread [] threads = new Thread[1000];

    /*-----------------------------------Atomic类-----------------------------------*/
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(() ->{
        for (int j = 0; j < 100000; j++) {
          count1.incrementAndGet();
        }
      });
    }
    long start = System.currentTimeMillis();
    for (Thread t : threads) t.start();
    for (Thread t : threads) t.join();
    long end = System.currentTimeMillis();
    System.out.println("Atomic:" + count1.get() +"-----time:" +(end - start));

    /*----------------------------------synchronized---------------------------------*/
    Object lock = new Object();
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(new Runnable() {
        @Override
        public void run() {
          for (int j = 0; j < 100000; j++) {
            synchronized (lock) {
              count2++;
            }
          }
        }
      });
    }
    long start2 = System.currentTimeMillis();
    for (Thread t : threads) t.start();
    for (Thread t : threads) t.join();
    long end2 = System.currentTimeMillis();
    System.out.println("synchronized:" + count1.get() +"-----time:" +(end2 - start2));

    /*-------------------------------------LongAdder----------------------------------*/
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(() ->{
        for (int j = 0; j < 100000; j++) {
          count3.increment();
        }
      });
    }
    long start3 = System.currentTimeMillis();
    for (Thread t : threads) t.start();
    for (Thread t : threads) t.join();
    long end3 = System.currentTimeMillis();
    System.out.println("LongAdder:" + count1.get() +"-----time:" +(end3 - start3));
  }
}
 /*----------------------------------运行结果---------------------------------*/
Atomic:100000000-----time:2096synchronized:100000000-----time:5765LongAdder:100000000-----time:515

从以上的结果来看并发量达到一定程度运行效率:LongAdder > AtomicLong > synchronized; 这个还只是一个粗略的测试,具体使用还要根据实际情况。

为什么AtomicLong的效率比synchronized的效率高?

AtomicLong的底层使用的是CAS操作(无锁优化),而synchronized虽然底层做了优化但是并发量达到一定层度,存在锁的膨胀,最终会变成重量级锁,需要向操作系统申请锁资源,所以synchronized的效率慢一点合情合理。

为什么LongAdder的效率比AtomicLong的效率高?

因为LongAdder使用了分段锁的概念,效率比AtomicLong的效率高。

分段锁的意思就是用一个数组把线程分成若干组,然后运行结束后把结果累加起来,例如你有1000个线程,数组的长度为4,那就把0-250个放到数组的第0位,以此类推,然后把四个数组中线程的计算结果累加,这样会很大程度上节省时间,从而提高效率。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java concurrency之AtomicLongFieldUpdater原子类_动力节点Java学院整理

    AtomicLongFieldUpdater介绍和函数列表 AtomicLongFieldUpdater可以对指定"类的 'volatile long'类型的成员"进行原子更新.它是基于反射原理实现的. AtomicLongFieldUpdater函数列表 // 受保护的无操作构造方法,供子类使用. protected AtomicLongFieldUpdater() // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值. long addAndGet(T obj, lo

  • java并发之AtomicInteger源码分析

    问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicInteger是java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作. 还记得Unsafe吗?点击链接直达[java Unsafe详细解析] 原子操作 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何

  • Java concurrency之AtomicReference原子类_动力节点Java学院整理

    AtomicReference介绍和函数列表 AtomicReference是作用是对"对象"进行原子操作. AtomicReference函数列表 // 使用 null 初始值创建新的 AtomicReference. AtomicReference() // 使用给定的初始值创建新的 AtomicReference. AtomicReference(V initialValue) // 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值. boolean compare

  • Java中对AtomicInteger和int值在多线程下递增操作的测试

    Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下: java.util.concurrent.atomic.AtomicBoolean; java.util.concurrent.atomic.AtomicInteger; java.util.concurrent.atomic.AtomicLong; java.util.concurrent.atomic.AtomicReference; 下面是一个对比  AtomicInteger 与 普通 int 值在多线

  • 深入了解Java atomic原子类的使用方法和原理

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicInteger atomicInteger=new AtomicInteger(); for(int i=0;i<10;i++){ Thread t=new Thread(new AtomicTest(atomicInteger)); t.start(); try { t.join(0); } catch (I

  • 浅谈Java中的atomic包实现原理及应用

    1.同步问题的提出 假设我们使用一个双核处理器执行A和B两个线程,核1执行A线程,而核2执行B线程,这两个线程现在都要对名为obj的对象的成员变量i进行加1操作,假设i的初始值为0,理论上两个线程运行后i的值应该变成2,但实际上很有可能结果为1. 我们现在来分析原因,这里为了分析的简单,我们不考虑缓存的情况,实际上有缓存会使结果为1的可能性增大.A线程将内存中的变量i读取到核1算数运算单元中,然后进行加1操作,再将这个计算结果写回到内存中,因为上述操作不是原子操作,只要B线程在A线程将i增加1的

  • Java多线程Atomic包操作原子变量与原子类详解

    在阅读这篇文章之前,大家可以先看下<Java多线程atomic包介绍及使用方法>,了解atomic包的相关内容. 一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的.通常来说,原子指令由硬件提供,供软件来实现原子方法(某个线程进入该方法后,就不会被中断,直到其执行完成) 在x86平台上,CPU提供了在指令执行期间对总线加锁的手段.

  • Java Atomic类及线程同步新机制原理解析

    一.为什么要使用Atomic类? 看一下下面这个小程序,模拟计数,创建10个线程,共同访问这个int count = 0 :每个线程给count往上加10000,这个时候你需要加锁,如果不加锁会出现线程安全问题,但是使用AtomicInteger之后就不用再做加锁的操作了,因为AtomicInteger内部使用了CAS操作,直接无锁往上递增,有人会问问什么会出现无锁操作,答案只有一个:那就是快呗: 下面是AtomicInteger的使用方法: package com.example.demo.t

  • java线程池中Worker线程执行流程原理解析

    目录 引言 Worker类分析 runWorker(Worker)方法 getTask()方法 beforeExecute(Thread, Runnable)方法 afterExecute(Runnable, Throwable)方法 processWorkerExit(Worker, boolean)方法 tryTerminate()方法 terminated()方法 引言 在<[高并发]别闹了,这样理解线程池执行任务的核心流程才正确!!>一文中我们深度分析了线程池执行任务的核心流程,在Th

  • Java并发之传统线程同步通信技术代码详解

    本文研究的主要是Java并发之传统线程同步通信技术的相关代码示例,具体介绍如下. 先看一个问题: 有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次--如此往返执行50次. 看完这个问题,很明显要用到线程间的通信了, 先分析一下思路:首先肯定要有两个线程,然后每个线程中肯定有个50次的循环,因为每个线程都要往返执行任务50次,主线程的任务是执行5次,子线程的任务是执行10次.线程间通信技术主要用到wait()方法和notify()方法.wait()方

  • Java线程状态运行原理解析

    这篇文章主要介绍了Java线程状态运行原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码实例如下 package com.fgy.demo05; /** * 等待唤醒案例:线程之间通信 * 注意: * 同步使用的锁对象必须唯一 * 只有锁对象才能调用wait和notify()/notifyAll()方法 */ public class Demo1WaitAndNotify { public static void main(Strin

  • C#使用Monitor类实现线程同步

    一.简介 Lock关键字是Monitor的一种替换用法,lock在IL代码中会被翻译成Monitor. lock (obj) { //代码段 } //就等同于 Monitor.Enter(obj); //代码段 Monitor.Exit(obj); Monitor的常用属性和方法: Enter(Object) 在指定对象上获取排他锁. Exit(Object) 释放指定对象上的排他锁. Pulse 通知等待队列中的线程锁定对象状态的更改. PulseAll 通知所有的等待线程对象状态的更改. T

  • Java 并发编程:volatile的使用及其原理解析

    Java并发编程系列[未完]: •Java 并发编程:核心理论 •Java并发编程:Synchronized及其实现原理 •Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) •Java 并发编程:线程间的协作(wait/notify/sleep/yield/join) •Java 并发编程:volatile的使用及其原理 一.volatile的作用 在<Java并发编程:核心理论>一文中,我们已经提到过可见性.有序性及原子性问题,通常情况下我们可以通过Synchroniz

  • Java重写(Override)与重载(Overload)区别原理解析

    这篇文章主要介绍了Java重写(Override)与重载(Overload)区别原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类能够根据需要实现父类的方法. 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常.例如: 父类的一个

  • Netty分布式高性能工具类异线程下回收对象解析

    目录 异线程回收对象 跟到pushLater方法中 跟到allocate方法中 回到pushLater方法中 简单看下link的类的定义 回到pushLater方法中 前文传送门:Netty分布式高性能工具类同线程下回收对象解析 异线程回收对象 就是创建对象和回收对象不在同一条线程的情况下, 对象回收的逻辑 我们之前小节简单介绍过, 异线程回收对象, 是不会放在当前线程的stack中的, 而是放在一个WeakOrderQueue的数据结构中, 回顾我们之前的一个图: 8-6-1 相关的逻辑, 我

  • Java实体类实现链式操作实例解析

    这篇文章主要介绍了Java实体类实现链式操作实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 原来是这么写bean的,单纯的使用get.set方法,再加一个toString package Model; /** * @author: Davion * @date: 2019/12/11 * @description: */ public class User { private Integer id; private String nam

  • python线程join方法原理解析

    这篇文章主要介绍了python线程join方法原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 几个事实 1 python 默认参数创建线程后,不管主线程是否执行完毕,都会等待子线程执行完毕才一起退出,有无join结果一样 2 如果创建线程,并且设置了daemon为true,即thread.setDaemon(True), 则主线程执行完毕后自动退出,不会等待子线程的执行结果.而且随着主线程退出,子线程也消亡. 3 join方法的作用是阻

随机推荐