欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java并发编程-12个原子类

程序员文章站 2023-08-31 08:17:37
背景 多线程更新变量的值,可能得不到预期的值,当然增加syncronized关键字可以解决线程并发的问题。 这里提供另外一种解决问题的方案,即位于 java.util.concurrent.atomic包下的原子操作类,提供了一种用法简单,性能高效,线程安全的更新变量的方式。 其它两个附带的类顺带看 ......

背景

多线程更新变量的值,可能得不到预期的值,当然增加syncronized关键字可以解决线程并发的问题。
这里提供另外一种解决问题的方案,即位于 java.util.concurrent.atomic包下的原子操作类,提供了一种用法简单,性能高效,线程安全的更新变量的方式。

java并发编程-12个原子类

其它两个附带的类顺带看了一下:

longaddr 多线程先的sum操作
longaccomulator 多线程下的函数式操作,性能低于atomiclong,主要是函数式的支持;

简单分类:

java并发编程-12个原子类

基本类型原子类

使用原子的方式更新基本类型,包括:

  • atomicboolean
  • atomicinteger
  • atomiclong

核心方法:

直接看源码了。

java并发编程-12个原子类

类签名:
public class atomicinteger extends number implements java.io.serializable {}

方法 功能说明
构造方法 两个构造方法,不传或者传入值
get方法 get()获取值;对应的有set(int)方法,layzyset(int) 懒设置
getandadd(int) 获得老值然后增加一个数字, 对应的有addandget(int)增加一个数字并返回新值
getandset(int) 获得老值然后更新为新值
getandincreament() 获得老值然后+1,对应的有increamentandget() +1然后返回新值
getanddecrement() 获得老值然后-1 ,对应的有decrementandget() -1然后返回新值
getandupdate(intunaryoperator) 获取老值然后执行一个函数得到新值并设置,对应的有updateandget(intunaryoperator) 先执行内置函数式接口再返回新值
getandaccumulate(int,intbinaryoperator) 获取老值,然后把老值和第一个参数进行函数运算的返回值并设置 ,对应的有accumulateandget(int,intbinaryoperator) 执行运算然后返回新值
compareandset(int,int) 对比如果跟预期值相等则设置为新值,对应的有weakcompareandset(int,int)这个是不保证顺序设置
tostring 返回数字的字符串形式
number继承过来的方法 longvalue(),bytevalue()直接做了类型转换
object继承过来的方法 直接沿用object的方法

底层是基于 unsafe来实现,基于cas来原子性;

来研究一下unsafe的实现源码:

    /**
     * atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final int getanddecrement() {
        return unsafe.getandaddint(this, valueoffset, -1);
    }
public final int getandaddint(object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getintvolatile(var1, var2);
        } while(!this.compareandswapint(var1, var2, var5, var5 + var4));

        return var5;
    }

大致的处理流程是:死循环,对比; 也就是cas;
利用了volatile的特性,多线程的变量可见性;

原子数组

通过原子的方式更新数组中的某个元素;

包含3个类:

  • atomicintegerarray
  • atomicintegerlongarray
  • atomicreferencearray

抓一个类来分析研究一下:

public class atomicintegerarray implements java.io.serializable {}

java并发编程-12个原子类

方法 说明
构造方法 public atomicintegerarray(int length),public atomicintegerarray(int[] array)这里会做一个clone,不影响传入的数组的值
length 得到内部数组的长度
get,set,layziset 获取,设置,懒设置
compareandset,weakcompareandset cas操作, weak方法不保证操作的顺序性
getandadd,getandupdate,getandaccumulate 有反向的方法,就是先计算,然后返回新值
tostring 打印出数组【数字1,数字2】

原子类型的操作比较特殊一点:

    /**
     * atomically adds the given value to the element at index {@code i}.
     *
     * @param i the index
     * @param delta the value to add
     * @return the previous value
     */
    public final int getandadd(int i, int delta) {
        return unsafe.getandaddint(array, checkedbyteoffset(i), delta);
    }
 private long checkedbyteoffset(int i) {
        if (i < 0 || i >= array.length)
            throw new indexoutofboundsexception("index " + i);

        return byteoffset(i);
    }

    private static long byteoffset(int i) {
        return ((long) i << shift) + base;
    }

这块获取数组中的值时候用到了一个移位操作;

更新引用

原子更新类atomicinterger只能更新一个变量,如果要更新多个不同的变量就要用到原子更新引用 类型提供的类;

  • atomicreference 更新引用类型
  • atomicreferencefieldupdater 更新引用类型的字段
  • atomicmarkablererence 更新带有标志位的引用类型

以atomicreference为例子:

签名:public class atomicreference implements java.io.serializable{}

方法:

java并发编程-12个原子类

方法 说明
构造方法 public atomicreference(v initialvalue) 带初始值;public atomicreference()
get,set,lazyset 设置,获取,懒设置
compareandset,weakcompareandset cas操作,weak方法不保证顺序
getandset,getandupdate,getandaccumulate 有反向的操作
tostring 打印出里面的对象

底层分析:

    /**
     * atomically sets to the given value and returns the old value.
     *
     * @param newvalue the new value
     * @return the previous value
     */
    @suppresswarnings("unchecked")
    public final v getandset(v newvalue) {
        return (v)unsafe.getandsetobject(this, valueoffset, newvalue);
    }

利用了unsafe提供的特性保证了原子操作;

原子更新字段

需要原子的更新某个类的某个字段,需要用到原子更新字段类;

  • atomicintegerfiledupdater 不用多说,原子更新类的interger字段
  • atomiclongfieldupdater 不用多说,原子更新类的long字段
  • atomicstampedreference 原子更新带版本号的引用类型,可以原子的更新引用和引用的版本号,解决aba问题;

使用要点:

  1. 每次必须使用静态方法 newupdater创建一个更新器,设置类和属性;
  2. 更新的类的属性必须使用 public volatile修饰;
package com.cocurrenttest.atomictest;

import lombok.allargsconstructor;
import lombok.builder;
import lombok.data;
import lombok.noargsconstructor;

/**
 * 说明:人实体
 * @author carter
 * 创建时间: 2019年12月06日 19:27
 **/
@data
@noargsconstructor
@allargsconstructor
@builder
public class person {

    private string name;

    //注意,只能是int,integer会报错哦
    public volatile int age;
}
package com.cocurrenttest.atomictest;

import java.util.concurrent.atomic.atomicintegerfieldupdater;

/**
 * 说明:todo
 * @author carter
 * 创建时间: 2019年12月06日 19:26
 **/

public class testatomicintegerupdater {

    public static void main(string[] args) {

        final atomicintegerfieldupdater<person> personatomicintegerfieldupdater = atomicintegerfieldupdater.newupdater(person.class, "age");

        person person = person.builder().name("lifuchun").age(30).build();

        personatomicintegerfieldupdater.addandget(person,1);

        final int age = personatomicintegerfieldupdater.get(person);
        system.out.println(age);

        assert age==31 : "更新失败";


    }

}

小结

原子操作类都介绍了一下,适当的场景的话,简单的说一下我使用过的两个场景:

  1. 多任务去数据copy的时候的对账,计算出总的修改行数,或者迁移的订单总金额,方便两边对比;
  2. 流式操作的lambda表达式里面需要传入的局部变量为final,但是 一般的类型在方法中不是final的,还需要在中间过程中修改,ide提示可以使用原子类包装,然后带上final去修改;

java并发编程-12个原子类

package com.cocurrenttest.atomictest;

import java.util.arrays;
import java.util.list;
import java.util.concurrent.atomic.atomicreference;
import java.util.stream.collectors;

/**
 * 说明:todo
 * @author carter
 * 创建时间: 2019年12月06日 19:36
 **/

public class teststream {

    public static void main(string[] args) {

        integer age = 25 ;
      final string name="b";

      //some condition to change
      name ="bbb";

        final list<person> personlist = arrays.aslist(
                person.builder().name("aaa").age(10).build(),
                person.builder().name("bbb").age(20).build(),
                person.builder().name("ccc").age(30).build()
        )
                .stream()
                .filter(item -> item.getage() >= age)
                .filter(item->item.getname().contains(name))
                .collect(collectors.tolist());

        system.out.println(personlist);

    }


    public static void main2(string[] args) {

        integer age = 25 ;
      final atomicreference<string> name=new atomicreference<>("b");

        //some condition to change
        name.set("bbb");;

        final list<person> personlist = arrays.aslist(
                person.builder().name("aaa").age(10).build(),
                person.builder().name("bbb").age(20).build(),
                person.builder().name("ccc").age(30).build()
        )
                .stream()
                .filter(item -> item.getage() >= age)
                .filter(item->item.getname().contains(name.get()))
                .collect(collectors.tolist());

        system.out.println(personlist);

    }

}

原创不易,转载请注明出处。