目录

Atomic

1. AtomicInteger

a. 多线程并发访问问题

b. 用 AtomicInteger 类解决

2. AtomicIntegerArray

a. 多线程并发访问问题

b. 用 AtomicIntegerArray 类解决

相关问题


Atomic

在 java.util.concurrent.atomic 包下定义了一些对“变量”操作的“原子类”,它们可以保证对“变量”操作的:原子性、有序性、可见性:

  • java.util.concurrent.atomic.AtomicInteger:对 int 变量操作的“原子类”;
  • java.util.concurrent.atomic.AtomicLong:对 long 变量操作的“原子类”;
  • java.util.concurrent.atomic.AtomicBoolean:对 boolean 变量操作的“原子类”;

1. AtomicInteger

a. 多线程并发访问问题

class MyThread extends Thread {public static volatile int a = 0;@Overridepublic void run() {for (int i = 0; i < 10000; i++) {//线程1:取出a的值a=0(被暂停)a++;//写回}System.out.println("修改完毕!");}
}public class Test {public static void main(String[] args) throws InterruptedException {//1.启动两个线程MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();Thread.sleep(1000);System.out.println("获取a最终值:" + MyThread.a);//最终结果仍然不正确。}
}
/*
输出
修改完毕!
修改完毕!
获取a最终值:14791*/

b. 用 AtomicInteger 类解决

import java.util.concurrent.atomic.AtomicInteger;class MyThread extends Thread {//public static volatile int a = 0;//不直接使用基本类型变量//改用"原子类"public static AtomicInteger a = new AtomicInteger(0);@Overridepublic void run() {for (int i = 0; i < 10000; i++) {// a++;a.getAndIncrement();//先获取,再自增1:a++}System.out.println("修改完毕!");}
}public class Test {public static void main(String[] args) throws InterruptedException {//1.启动两个线程MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();Thread.sleep(1000);System.out.println("获取a最终值:" + MyThread.a.get());}
}
/*
输出
修改完毕!
修改完毕!
获取a最终值:20000*/

c. 工作原理:CAS 机制

在 Unsafe 类中,调用了一个:compareAndSwapInt() 方法,此方法的几个参数:

此方法使用了一种"比较并交换(Compare And Swap)"的机制,它会用 var1 和 var2 先获取内存中 AtomicInteger 中的值,然后和传入的,之前获取的值 var5 做一下比较,也就是比较当前内存的值和预期的值是否一致,如果一致就修改为 var5 + var4,否则就继续循环,再次获取 AtomicInteger 中的值,再进行比较并交换,直至成功交换为止;compareAndSwapInt() 方法是"线程安全"的;我们假设两个线程交替运行的情况,看看它是怎样工作的:

CAS 机制也被称为乐观锁机制、自旋锁机制,因为大部分比较的结果为 true,就直接修改了。只有少部分多线程并发的情况会导致 CAS 失败,而再次循环;

2. AtomicIntegerArray

常用的数组操作的原子类,它们可以解决数组的多线程并发访问的安全性问题:

a. 多线程并发访问问题

class MyThread extends Thread {private static int[] intArray = new int[1000];//不直接使用数组@Overridepublic void run() {for (int i = 0; i < getIntArrayLength(); i++) {intArray[i]++;}}public static int getIntArray(int i) {return intArray[i];}public static int getIntArrayLength() {return intArray.length;}
}public class Test {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {new MyThread().start();//创建1000个线程,每个线程为数组的每个元素+1}Thread.sleep(1000 * 5);//让所有线程执行完毕System.out.println("主线程休息5秒醒来");for (int i = 0; i < MyThread.getIntArrayLength(); i++) {System.out.println(MyThread.getIntArray(i));}}
}
/*
部分输出
...
999
999
1000
1000
...*/

b. 用 AtomicIntegerArray 类解决

import java.util.concurrent.atomic.AtomicIntegerArray;class MyThread extends Thread {private static int[] intArray = new int[1000];//定义一个数组//改用原子类,使用数组构造public static AtomicIntegerArray arr = new AtomicIntegerArray(intArray);@Overridepublic void run() {for (int i = 0; i < arr.length(); i++) {arr.addAndGet(i, 1);//将i位置上的元素+1}}
}public class Test {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {new MyThread().start();}Thread.sleep(1000 * 5);//让所有线程执行完毕System.out.println("主线程休息5秒醒来");for (int i = 0; i < MyThread.arr.length(); i++) {System.out.println(MyThread.arr.get(i));}}
}
/*
输出
1000
1000
1000
1000
...*/

相关问题

1:为什么会出现Atomic类

  在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 稍有经验的同学都知道这种写法是线程不安全的。为了达到线程安全的目的,我们通常会用synchronized来修饰对应的代码块。现在我们有了新的方法,就是使用J.U.C包下的atomic类。

2:Atomic类的原理是什么呢

一句话来说,atomic类是通过自旋CAS操作volatile变量实现的。CAS是compare and swap的缩写,即比较后(比较内存中的旧值与预期值)交换(将旧值替换成预期值)。它是sun.misc包下Unsafe类提供的功能,需要底层硬件指令集的支撑。使用volatile变量是为了多个线程间变量的值能及时同步。

3:为什么使用Atomic类

按理来说,使用synchroized已经能满足功能需求了。为什么还会有这个类呢?那肯定是性能的问题了。在JDK1.6之前,synchroized是重量级锁,即操作被锁的变量前就对对象加锁,不管此对象会不会产生资源竞争。这属于悲观锁的一种实现方式。而CAS会比较内存中对象和当前对象的值是否相同,相同的话才会更新内存中的值,不同的话便会返回失败。这是乐观锁的一中实现方式。这种方式就避免了直接使用内核状态的重量级锁。但是在JDK1.6以后,synchronized进行了优化,引入了偏向锁,轻量级锁,其中也采用了CAS这种思想,效率有了很大的提升。

4:Atomic类的缺点

1)ABA问题:对于一个旧的变量值A,线程2将A的值改成B又改成可A,此时线程1通过CAS看到A并没有变化,但实际A已经发生了变化,这就是ABA问题。解决这个问题的方法很简单,记录一下变量的版本就可以了,在变量的值发生变化时对应的版本也做出相应的变化,然后CAS操作时比较一下版本就知道变量有没有发生变化。atomic包下AtomicStampedReference类实现了这种思路。Mysql中Innodb的多版本并发锁也是这个原理。

2)自旋问题:atomic类会多次尝试CAS操作直至成功或失败,这个过程叫做自旋。通过自旋的过程我们可以看出自旋操作不会将线程挂起,从而避免了内核线程切换,但是自旋的过程也可以看做CPU死循环,会一直占用CPU资源。这种情形在单CPU的机器上是不能容忍的,因此自旋一般都会有个次数限制,即超过这个次数后线程就会放弃时间片,等待下次机会。因此自旋操作在资源竞争不激烈的情况下确实能提高效率,但是在资源竞争特别激烈的场景中,CAS操作会的失败率就会大大提高,这时使用中重量级锁的效率可能会更高。当前,也可以使用LongAdder类来替换,它则采用了分段锁的思想来解决并发竞争的问题。

Java并发编程—Atomic原子类相关推荐

  1. Java并发编程实战~原子类

    对于简单的原子性问题,还有一种无锁方案,先看看如何利用原子类解决累加器问题. public class Test {AtomicLong count = new AtomicLong(0);publi ...

  2. Java高并发编程:原子类

    1. 并发编程概念 原子性 一个操作不能被再拆分了:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.一个很经典的例子就是银行账户转账问题. 增量操作符++,不是原 ...

  3. 「死磕Java并发编程」说说Java Atomic 原子类的实现原理

    <死磕 Java 并发编程>系列连载中,大家可以关注一波. 「死磕 Java 并发编程」阿里二面,面试官:说说 Java CAS 原理? 「死磕 Java 并发编程」面试官:说说什么是 J ...

  4. atomic原子类实现机制_并发编程:并发操作原子类Atomic以及CAS的ABA问题

    本文基于JDK1.8 Atomic原子类 原子类是具有原子操作特征的类. 原子类存在于java.util.concurrent.atmic包下. 根据操作的数据类型,原子类可以分为以下几类. 基本类型 ...

  5. Java并发编程,无锁CAS与Unsafe类及其并发包Atomic

    为什么80%的码农都做不了架构师?>>>    我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其 ...

  6. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  7. Java并发编程包中atomic的实现原理

    转载自   Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿,作者[林湾村龙猫]最近在阅读Java源码,这一篇是他关于并发包中atomic类的源码阅读的总结.Hollis做了一点点修 ...

  8. Java多线程进阶面试-Atomic 原子类

    1.介绍一下 Atomic 原子类 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的. ...

  9. atomic原子类实现机制_深入了解Java atomic原子类的使用方法和原理

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...

最新文章

  1. nvidia旧版驱动_N卡用户注意:老版驱动存在5个高危漏洞,赶紧更新
  2. QUARK的增强版C-QUARK问世,有效提升蛋白质结构从头预测精度
  3. 【收藏】IDEA中MAVEN项目自动导入依赖的启动与取消
  4. Android 创建,删除,检测桌面快捷方式
  5. 数据结构--栈--顺序栈/链式栈(附: 字符括号合法配对检测)
  6. python实用黑客脚本_Python黑客攻防(十六)编写Dos脚本,进行容易攻击演示
  7. 基于VUE2.0的分页插件
  8. 世界上最简单的会计书-笔记
  9. flutter flutter_screenutil Looking up a deactivated widget‘s ancestor is unsafe.
  10. GTK:Gdk-CRITICAL **: IA__gdk_draw_pixbuf: assertion ‘GDK_IS_DRAWABLE (drawable)‘ failed
  11. STM32——TIM1的TIM1_CH1N通道PWM初始化
  12. Tesseract-OCR对图像和PDF进行光学文字识别
  13. 我女儿说要看雪,于是我默默的拿起了键盘,下雪咯,程序员就是可以为所欲为!
  14. VSCode 出现 正在保存“xxxx.vue”: 从 “‘Vetur‘, ‘ESLint‘“(configure)中获取代码操作。
  15. 走近Ts,用了爽,用后一直爽(一)
  16. 一张通往计算机世界的地图
  17. 使用Tushare进行金融时间序列分析研究
  18. 内存条玄学之四槽插满就这么难么?
  19. formatDate方法
  20. 商业地图 | 从肯德基地理分布浅析商业选址

热门文章

  1. 简单的协议应用-代理模式
  2. matlab2012b帮助中字体大小调整
  3. dealloc 的水,很深?
  4. 更改列表的默认项标记的颜色、大小等样式的解决办法
  5. Api文档生成工具与Api文档的传播(pdf)
  6. 利用js实现popup弹窗
  7. VS2012 打包部署程序
  8. new Date('2014/04/30') 和 new Date('2014-04-30') 的区别
  9. python内置函数中的zip,max,min函数例子
  10. Ubuntu18.04提示wifi无法连接