第七章 Java中的13个原子操作类

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i = 1;A线程更新i + 1,B线程也更新i + 1,经过两个线程操作之后可能i不等于3,而是等于2,。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i.

而Java从JDK1.5开始提供了java.util.concurrent.atomic(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。

原子更新基本类型类

使用原子的方式更新基本类型,Atomic包提供了以下3个类:

  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新长整型

以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行详解,AtomicInteger的常用方法如下:

(1)int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果

(2)boolean compareAndSet(int expect, int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值

(3)int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值

(4)void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值

(5)int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值

AtomicInteger示例代码如下

public class AtomicIntegerTest {

    static AtomicInteger atomicInteger = new AtomicInteger(1);

    public static void main(String[] args) {
        System.out.println(atomicInteger.getAndIncrement());
        System.out.println(atomicInteger.get());
    }
}

输出1 2

那么getAndIncrement()是如何实现原子操作的呢?让我们一起分析其实现原理

  public final int getAndIncrement(){
        for(;;){
            int current = get();
            int next = current + 1;
            if(compareAndSet(current, next)) return current;
        }
    }

    public final boolean compareAndSet(int expect, int update){
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

源码中for循环体的第一步先取得AtomicInteger里存储的数值,第二步对AtomicInteger的当前数值进行加1操作,关键的第三步调用compareAndSet方法来进行原子更新操作,该方法先检查当前数值是否等于current,等于意味着AtomicInteger的值没有被其他线程修改过,则将AtomicInteger的当前值更新成next的值,如果不等compareAndSet方法会返回false,程序会进入for循环重新进行compareAndSet操作。

Atomic包提供了3种基本类型的原子更新,但是Java的基本类型里还有char、float和double等。

那么如何原子的更新其他的基本类型呢?

Atomic包里的类基本都是使用Unsafe实现的,我们发现Unsafe只提供了3种CAS方法:compareAndSwapObject、compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现它是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新char、float和double变量也可以用类似的思路来实现。

原子更新数组

通过原子的方式更新数组里的某个元素,Atomic包提供了以下4个类:

  • AtomicIntegerArray:原子更新整型数组里的元素
  • AtomicLongArray:

原文地址:https://www.cnblogs.com/hzzjj/p/9738076.html

时间: 2024-11-09 14:34:12

第七章 Java中的13个原子操作类的相关文章

【多线程与并发】Java中的12个原子操作类

从JDK1.5开始,Java提供了java.util.concurrent.atomic包,该包中的原子操作类提供了一种使用简单.性能高效(使用CAS操作,无需加锁).线程安全地更新一个变量的方式. `java.util.concurrent.atomic`包中的类.png 根据变量类型的不同,Atomic包中的这12个原子操作类可以分为4种类型: ①原子更新基本类型:AtomicBoolean.AtomicInteger.AtomicLong ②原子更新数组:AtomicIntegerArra

《Java并发编程的艺术》 第9章 Java中的线程池

第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可管理性.线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配.调优和监控. 9.1 线程池的实现原理 当提交一个新任务到线程池时,线程池的处理流程如下: 1)线程池判断核心线程池里的线程是否都在执行任务.如果不是,则创建

【转 Java 中的内部类和匿名类

 Java 中的内部类和匿名类 2008-10-16 13:47:41 标签:Java 内部类 匿名类 休闲 职场 Java 中的内部类和匿名类* 为什么需要内部类? Java 内部类有什么好处?为什么需要内部类? 首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口.由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能. 不过你可能要质疑,更改一

重温java中的String,StringBuffer,StringBuilder类

任何一个系统在开发的过程中, 相信都不会缺少对字符串的处理. 在 java 语言中, 用来处理字符串的的类常用的有 3 个: String.StringBuffer.StringBuilder. 它们的异同点: 1) 都是 final 类, 都不允许被继承; 2) String 长度是不可变的, StringBuffer.StringBuilder 长度是可变的; 3) StringBuffer 是线程安全的, StringBuilder 不是线程安全的. String 类已在上一篇随笔 小瓜牛

java中必须知道的常用类

1.Java的包装类 基本数据类型我们都很熟悉,例如:int.float.double.boolean.char等,基本数据类型不具备对象的特征,不能调用方法,一般能实现的功能比较简单,为了让基本数据类型也具备对象的特性,Java为每个数据类型都提供了一个包装类,这样我们就可以像操作对象一样,操作这些基本数据类型了 常见的包装类和基本类型对应关系如下: 包装类主要提供了两类方法: 1.进行多个类型之间的转换 2.将字符串和本类型及包装类相互转换 比如下面代码: 1 int i = 2; 2 In

Java中的集合和常用类

Java中的常用类: ? Object类 ? Math类 ? String类和StringBuffer类(字符串) ? 8种基本类型所对应的包装类 ? java.util包中的类——Date类 Object类: Object类是Java语言程序中所有类的父类,即承自Object类.Object类中包含了Java语言类中的所有的公共属性. ? toString()方法 ? equals()方法 ? getClass()方法 ? clone()方法 ? finalize()方法 枚举类: 用于储存变

java中常用的包、类、以及包中常用的类、方法、属性-----io包

由于最近有需要,所以下面是我整理的在开发中常用的包.类.以及包中常用的类.方法.属性:有需要的看看 java中常用的包.类.以及包中常用的类.方法.属性 常用的包 java.io.*; java.util.*; java.lang.*; java.math.*; java.sql.*; java.text.*; java.awt.*; javax.swing.*;   包名 接口 类 方法 属性 java.io.*; java.io.Serializable实现序列化 java.io.Buffe

java中的URLEncoder和URLDecoder类的联系与区别

今天碰到了这个问题,就查找了些资料总结下:java中的URLEncoder和URLDecoder类的联系与区别. 首先说这两者的联系与区别: URLEncoder是编码,URLDecoder是解码.两者的转换过程刚好是相反的.URLEncoder该类包含了将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法:URLDecoder该类包含了将 String 从 application/x-www-form-urlencoded MI

java中IO写文件工具类

下面是一些根据常用java类进行组装的对文件进行操作的类,平时,我更喜欢使用Jodd.io中提供的一些对文件的操作类,里面的方法写的简单易懂. 其中jodd中提供的JavaUtil类中提供的方法足够我们使用,里面的方法写的非常简练,例如append,read等方法,封装更好,更符合面向对象, 这里面我写的一些方法可多都是模仿jodd,从里面进行抽取出来的. /** * 获取路径文件夹下的所有文件 * @param path * @return */ public static File[] ge