首页 > 编程语言 >【并发编程】原子类

【并发编程】原子类

时间:2023-01-20 11:34:51浏览次数:50  
标签:10 Thread int 编程 原子 并发 new public


文章目录

  • ​​1.什么是原子类​​
  • ​​2.原子更新基本类型​​
  • ​​3.原子更新数组类型​​
  • ​​4.原子地更新属性​​

1.什么是原子类

(1)原子类简介

  • 一度认为原子是不可分割的最小单元,故原子类可以认为其操作都是不可分割的。

(2)为什么要有原子类

  • 对多线程访问一个变量,我们需要加锁,而锁是比较消耗性能的,JDK1.5之后,新增的原子操作类提供了一种用法简单、性能高效、线程安全的更新一个变量的方式,这些同类位于JUC包下的atomic包下,发展到JDK1.8,该包下共有17个类,囊括了原子更新基本类型、原子更新属性、原子更新引用。

(3)JDK1.8新增的原子类

  • DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64

2.原子更新基本类型

  • JDK1.8基本类型原子类有以下分类
  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新长整型
  • DoubleAdder:对Double的原子更新性能进行优化提升
  • LongAdder:对Long的原子更新性能进行优化提升
  • DoubleAccumulator:支持自定义运算
  • LongAccumulator:支持自定义运算
  • AtomicInteger的常见用法
  • AtomicInteger(int initialValue):创建一个AtomicInteger实例,初始值由参数指定。不带参的构造方法初始值为0。
  • int addAndGet(int delta) :以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果,与getAndAdd(int delta)相区分,从字面意思即可区分,前者返回相加后结果,后者先返回再相加。
  • boolean compareAndSet(int expect, int update) :如果当前值等于预期值,则以原子方式将该值设置为输入的值。
  • int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增前的值。
  • void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
  • int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。
  • 案例实战
  • 线程不安全的例子
public class Demo1 {

private static Integer num = 0;

void addUnSafe(){
//对atomicInteger进行++操作
num++;
}
public static void main(String[] args) throws InterruptedException {

Demo1 demo1 = new Demo1();

new Thread(()->{
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 1000; j++) {
demo1.addUnSafe();
}
}
}).start();

new Thread(()->{
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 1000; j++) {
demo1.addUnSafe();
}
}
}).start();
Thread.sleep(2000L);
System.out.println("num最后的值:"+num);
}

【并发编程】原子类_后端

  • AtomicInteger多线程调用
public class Demo1 {

private static AtomicInteger atomicInteger = new AtomicInteger(0);

void addSafe(){
//对atomicInteger进行++操作
atomicInteger.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 1000; j++) {
demo1.addSafe();
}
}
}).start();

new Thread(()->{
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 1000; j++) {
demo1.addSafe();
}
}
}).start();

Thread.sleep(2000L);
System.out.println("atomicInteger最后的值:"+atomicInteger.get());
}
}

【并发编程】原子类_字段_02

  • LongAccumulator的简单使用
public class Demo2 {

private static LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x*y,3L);

public static void main(String[] args) {
longAccumulator.accumulate(2);
System.out.println(longAccumulator.get());
}
}

【并发编程】原子类_原子类_03

3.原子更新数组类型

  • AtomicIntegerArray:对Integer数组类型的操作
  • AtomicLongArray:对Long数组类型的操作
  • AtomicReferenceArray:对对象数组类型的操作
  • AtomicIntegerArray的简单使用
public class Demo1 {

public static void main(String[] args) {

int[] arrInt = new int[]{2,3,4};

AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arrInt);

//给数组下标是1的元素+2
int i = atomicIntegerArray.addAndGet(1, 2);
System.out.println(i);

//支持自定义规则的操作
int i1 = atomicIntegerArray.accumulateAndGet(1, 2, (x, y) -> x > y ? x : y);
System.out.println(i1);
}
}

【并发编程】原子类_java_04

4.原子地更新属性

(1)原子的更新某个类的某个字段时,就需要使用原子更新字段类,Atomic包提供了一下4个类进行原子字段更新。

  • AtomicIntegerFieldUpdater:原子类int类型更改操作
  • AtomicLongFieldUpdater:原子类long类型更改操作
  • AtomicStampedReference:原子类引用类型更改操作,避免ABA问题,携带版本号
  • AtomicMarkableReference:原子类引用类型更改操作,避免ABA问题,携带boolean值
  • AtomicReferenceFieldUpdater:原子类引用类型更改操作

(2)AtomicReferenceFieldUpdater、AtomicLongFieldUpdater案例实战

public class Demo2 {
public static void main(String[] args) {
Student student = new Student(1L,"李祥");
//对long类型的数值改变
AtomicLongFieldUpdater<Student> atomicLongFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class,"id");
//将Student的id改成10
atomicLongFieldUpdater.compareAndSet(student,1L,10L);
//输出student的id
System.out.println("更改student的id:"+student.id);

//对引用类型的改变
AtomicReferenceFieldUpdater<Student, String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");

referenceFieldUpdater.compareAndSet(student,student.getName(),"李祥更改");

System.out.println("更改student的name:"+student.name);

}
}
public class Student {
volatile long id;
volatile String name;

public Student(long id, String name) {
this.id = id;
this.name = name;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

【并发编程】原子类_java_05

  • 使用原子更新类时注意的问题:
  • 字段必须是volatile类型的,在线程之间共享变量时保证立即可见。
  • 字段的描述类型是调用者与操作对象字段的关系一致,也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。
  • 只能是实例变量,不能是类变量,也就是说不能加static关键字。
  • 不能被final变量修饰的。
  • 对于AtomicIntegerFiledUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其他包装类型(Integer\Long)。
  • 修改包装类型就需要使用AtomicRefebceFieldUpdater。

(3)AtomicStampedReference使用

  • AtomciStampedReference是一个带有时间戳的对象引用,能够很好的解决CAS机制中的ABA问题。
  • 什么是ABA问题

【并发编程】原子类_原子类_06

  • ABA场景案例
public class ABADemo {
private static AtomicInteger index = new AtomicInteger(10);

public static void main(String[] args) {
new Thread(()->{
index.compareAndSet(10,11);
index.compareAndSet(11,10);
System.out.println(Thread.currentThread().getName()+": 进行了 10->11->10");
},"张三").start();

new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
boolean flag = index.compareAndSet(10, 12);
System.out.println(Thread.currentThread().getName()+":修改index结果:"+flag+",设置的新值是:"+index);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"李祥").start();
}
}

【并发编程】原子类_字段_07

  • AtomciStampedReference案例
  • compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)。
  • 第一个参数expectedReference:表示预期值。
  • 第二个参数newReference:表示要更新的值。
  • 第三个参数expectedStamp:表示预期的时间戳。
  • 第四个参数newStamp:表示要更新的时间戳。
public class AtomicStampedReferenceDemo {

private static AtomicInteger index = new AtomicInteger(10);

private static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference(10,1);

public static void main(String[] args) {

new Thread(()->{
System.out.println(Thread.currentThread().getName()+":当前版本号为:"+stampedReference.getStamp());
stampedReference.compareAndSet(10,11,stampedReference.getStamp(),stampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+":当前版本号为:"+stampedReference.getStamp());
stampedReference.compareAndSet(11,10,stampedReference.getStamp(),stampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+":当前版本号为:"+stampedReference.getStamp());
},"张三").start();

new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+":拿到的版本号为:"+stampedReference.getStamp());
boolean flag = stampedReference.compareAndSet(10, 12, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName()+":修改index结果:"+flag+",设置的新值是:"+stampedReference.getReference());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"李祥").start();

}
}

【并发编程】原子类_后端_08

  • 其实除了AtomicStampedReference类,还有一个原子类也可以解决,就是AtomicMarkableReference,它不是维护一个版本号,而是维护一个boolean类型的标记,用法没有AtomicStampedReference灵活。因此也只是在特定的场景下使用。


标签:10,Thread,int,编程,原子,并发,new,public
From: https://blog.51cto.com/u_15646271/6020580

相关文章

  • 读函数式编程思维笔记01_演化的语言
    1. 范式转变1.1. 学习一种全新的编程范式,困难并不在于掌握新的语言1.1.1. 真正考验人的,是怎么学会用另一种方式去思考1.2. 计算机科学的间歇式进步,好思路有时搁置......
  • 资深程序员在编程中有哪些特殊的习惯或方法?
    知乎上有一个问答:高级程序员在编程中有哪些特殊的习惯或方法?我是一个有着22年编程经验的老程序员,谈不上高级,我来谈谈自己的三点心得。一定要有自己的代码库以前有程......
  • 17种编程语言实现排序算法-快速排序
    开源地址https://gitee.com/lblbc/simple-works/tree/master/sort/1.安卓Java版privatestaticvoidsort(int[]array){sortMe(array,0,array.length-1);......
  • 链式编程
    目录一、什么是链式编程二、链式编程的实现(指针)三、链式编程(引用)一、什么是链式编程链式编程在C++中使用的地方很多,比如说输出的时候可以使用很多的<<,这就使用了链式编程......
  • redis高并发经典问题
    缓存穿透当用户访问的数据既不在缓存也不在数据库中时,就会导致每个用户查询都会“穿透”缓存“直抵”数据库。这种情况就称为缓存穿透。当高度发的访问请求到达时,缓存穿......
  • 【学懂Java】(四)面向对象编程-2
    一.局部变量和成员变量局部变量成员变量(全局变量)定义在方法中定义在方法外,类之内的变量栈内存中堆内存中局部变量没有默认值成员变量有默认值当前方法当前类的方法不同的方......
  • 查询并发程序是否启动跟踪功能-trc文件对数据库性能有
    SELECTICON_NAME,ROW_ID,USER_CONCURRENT_PROGRAM_NAME,ENABLED_FLAG,CONCURRENT_PROGRAM_NAME,DESCRIPTION,EXECUTION_OPTIONS,REQUEST_PRIORITY,......
  • 【学懂Java】(四)面向对象编程-1
    一.OOP:ObjectOrientedProgramming1.面向过程和面向对象面向过程面向对象区别事物比较简单,可以用线性的思维去解决事物比较复杂,使用简单的线性思维无法解决共同点1.面向......
  • 网络编程知识总结
    网络编程1、概述计算机网络:计算机网络是指将​​地理​​​位置不同的具有独立功能的多台​​计算机​​​及其外部设备,通过通信线路连接起来,在​​网络操作系统​​​,​​......
  • 【并发编程】锁机制
    文章目录​​1.锁的分类​​​​2.深入理解Lock接口​​​​3.自定义实现可重入锁​​1.锁的分类自旋锁:线程状态及上下文切换消耗系统资源,当访问共享资源的时间短,频繁上下文......