多线程学习——原子类

多线程学习——原子类

Scroll Down

多线程学习——原子类

1.什么是原子类,什么作用?

java.util.concurrent.atomic
原子操作:一组操作不可分割
相比于锁来说:原子的粒度更小、效率更高(除了高度竞争情况下)
原理:CAS

2.常见的原子类

1.基本类型原子类Atomic*

AtomicInteger
AtomicLong
AtomicBoolean

2.数组类型原子类Atomic*Array

AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray

3.引用类型原子类Atomic*Reference

AtomicReference
AtomicStampedReference
AtomicMarkableReference

4.升级类型原子类Atomic*FieldUpdater

AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater

5.累加器*Adder

LongAdder
DoubleAdder

6.累加器*Accumulator

LongAccumulator
DoubleAccumulator

3.基本类型原子类操作

以AtomicInteger为例:

int get() 获取当前值 
int getAndSet(int newValue)    以原子方式设置为给定值,并返回旧值 
int getAndIncrement()  获取当前值,并且自增
int getAndDecrement()  获取当前值,并且自减
boolean compareAndSet(int expect, int update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值 

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName: AtomicPractice
 * @Description: 原子类与普通变量的并发
 * @author: XP
 */
public class AtomicPractice implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        AtomicPractice atomicPractice = new AtomicPractice();
        Thread thread = new Thread(atomicPractice);
        Thread thread1 = new Thread(atomicPractice);
        thread.start();
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println("原子类"+atomicInteger.get());
        System.out.println("普通"+i);
    }
    /**原子类*/
    public static AtomicInteger atomicInteger=new AtomicInteger();
    public static volatile Integer i=0;
    public void incrementAtomic(){
        atomicInteger.getAndIncrement();
    }
    public void increment(){
        i++;
    }


    @Override
    public void run() {
        for (int j = 0; j <1000 ; j++) {
            incrementAtomic();
            increment();
        }
    }
}

效果:

原子类2000
普通1443

4.数组类型原子类操作

int get(int i) 获取位置 i 的当前值
int getAndDecrement(int i)  以原子方式将索引 i 的元素减 1。
int getAndAdd(int i, int delta)  以原子方式将给定值与索引 i 的元素相加。

例如:


import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * @ClassName: AtomicArrayPractice
 * @Description: 原子数组的练习
 * @author: XP
 */
public class AtomicArrayPractice {
    public static void main(String[] args) {
        AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(1000);
        Increment increment = new Increment(atomicIntegerArray);
        Decrement decrement = new Decrement(atomicIntegerArray);
        Thread[] threads = new Thread[1000];
        Thread[] threads1 = new Thread[1000];
        for (int i = 0; i < 1000; i++) {
            threads[i]=new Thread(increment);
            threads1[i]=new Thread(decrement);
            threads[i].start();
            threads1[i].start();
        }
        for (int i = 0; i < 1000; i++) {
            try {
                threads[i].join();
                threads1[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (int i = 0; i < 1000; i++) {
            System.out.println("每次的值"+atomicIntegerArray.get(i));
            if(atomicIntegerArray.get(i)!=0){
                System.out.println("并发不安全");
            }
        }

    }
}

class Increment implements Runnable{
    private AtomicIntegerArray atomicIntegerArray;

    public Increment(AtomicIntegerArray atomicIntegerArray) {
        this.atomicIntegerArray = atomicIntegerArray;
    }
    public void incrementArray(){
        for (int i = 0; i <atomicIntegerArray.length() ; i++) {
            atomicIntegerArray.getAndIncrement(i);
        }
    }

    @Override
    public void run() {
        incrementArray();
    }
}
class Decrement implements Runnable{
    private AtomicIntegerArray atomicIntegerArray;

    public Decrement(AtomicIntegerArray atomicIntegerArray) {
        this.atomicIntegerArray = atomicIntegerArray;
    }
    public void incrementArray(){
        for (int i = 0; i <atomicIntegerArray.length() ; i++) {
            atomicIntegerArray.getAndDecrement(i);
        }
    }

    @Override
    public void run() {
        incrementArray();
    }
}

结果:

每次的值0
每次的值0
每次的值0
.....

5.原子对象类操作

boolean compareAndSet(V expect, V update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
void set(V newValue)  设置为给定值。

import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName: SpinLock
 * @Description: AtomicReference实现自旋锁
 * @author: XP
 */
public class SpinLock {
    public static AtomicReference<Thread> atomicReference=new AtomicReference<>();
    public  void lock(){
        Thread thread = Thread.currentThread();
        while (!atomicReference.compareAndSet(null,thread)){
            System.out.println(Thread.currentThread().getName()+"获取锁失败,尝试获取");
        }
    }
    public void unLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
    }

    public static void main(String[] args) {
        SpinLock spinLock = new SpinLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始尝试获取自旋锁");
                spinLock.lock();
                System.out.println(Thread.currentThread().getName()+"获取到锁");
                spinLock.unLock();
            }
        };
        for (int i = 0; i < 1000; i++) {
            Thread thread =new Thread(runnable);
            thread.start();
        }


    }
}

效果:

Thread-327获取锁失败,尝试获取
Thread-327获取锁失败,尝试获取
Thread-327获取锁失败,尝试获取
Thread-327获取锁失败,尝试获取
Thread-947获取锁失败,尝试获取
Thread-947获取锁失败,尝试获取
Thread-947获取锁失败,尝试获取
Thread-947获取锁失败,尝试获取
Thread-947获取锁失败,尝试获取
Thread-69获取到锁
Thread-947获取锁失败,尝试获取
Thread-947获取到锁
Thread-327获取锁失败,尝试获取
Thread-327获取到锁
Thread-326获取锁失败,尝试获取
Thread-326获取到锁
Thread-51获取锁失败,尝试获取
Thread-51获取到锁
Thread-329获取锁失败,尝试获取
Thread-329获取到锁
Thread-328获取锁失败,尝试获取
Thread-328获取到锁
Thread-50获取锁失败,尝试获取
Thread-50获取到锁
Thread-886获取锁失败,尝试获取

6.原子升级类

static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) 
          使用给定字段为对象创建和返回一个更新器。 
int getAndIncrement(T obj) 以原子方式将此更新器管理的给定对象的当前值加 1。 

用于临时需要多并发操作的数据:


import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @ClassName: AtomicIntegerFieldUpdaterPractice
 * @Description: 模拟热点数据并发点赞
 * @author: XP
 */
public class AtomicIntegerFieldUpdaterPractice implements Runnable{
    static HotNews hotNewsA;
    static HotNews hotNewsB;
    public static AtomicIntegerFieldUpdater<HotNews> atomicIntegerFieldUpdater=AtomicIntegerFieldUpdater.newUpdater(HotNews.class,"likes");

    @Override
    public void run() {
        for (int i = 0; i <10000 ; i++) {
            hotNewsA.likes++;
            atomicIntegerFieldUpdater.getAndIncrement(hotNewsB);
        }
    }

    public static class HotNews{
        volatile int likes;
    }

    public static void main(String[] args) {
        hotNewsA=new HotNews();
        hotNewsB=new HotNews();
        AtomicIntegerFieldUpdaterPractice atomicIntegerFieldUpdaterPractice=new AtomicIntegerFieldUpdaterPractice();
        Thread thread = new Thread(atomicIntegerFieldUpdaterPractice);
        Thread thread1 = new Thread(atomicIntegerFieldUpdaterPractice);
        thread.start();
        thread1.start();
        try {
            thread.join();
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("原子类"+hotNewsB.likes);
        System.out.println("普通"+hotNewsA.likes);
    }
}

效果

原子类20000
普通18846

注意:

用于升级的字段必须是volatile修饰 且不能static修饰

7.Adder

java8新加入的类,用于解决atomic原子类效率低的问题,使用了多段锁的原理
atomic原子类效率低的原因:atomic原子类每次更新数据都需要将自己的数据刷新到主内存,然后其他线程每次都需要将主内存的数据读出来,每次都做了同步
Adder原理:每个线程有自己的计数器,仅用来在自己线程内计数,即分段累加(内部有一个base变量和一个cell[]数组共同参与计数),base变量:竞争不激烈,直接累加在该变量上,cell[]数组竞争激烈,各个线程分段累加到自己的槽cell[i]中
用法:
在低竞争下,atomicLong和LongAdder具有相似的特征,在竞争激烈下,longadder的预期吞吐量要高很多,但是消耗的空间也更多(cell[i])
LongAdder适合统计求和的常景,而且Longadder只提供了add方法,而AtomicLong有cas方法

7.*Accumulator数学类

用于大量数学运算,并行计算场景

8.CAS理论

CAS:基于CPU指令,在两个线程去同时操作一个变量的时候,当A发现变量的值于他期待的值相等就进行操作,然后B去看变量的值不是他期待的值就不进行操作,从而保证了数据的准确性
java如何利用cas实现原子操作:在unsafe类中的compareAndSwapint方法,方法中先想办法拿到变量value的内存的地址,在native方法中实现原子的比较和替换
image.png
image.png
缺点:自旋时候长 有可能出现更新问题(A和B期待的值一样,即A更新的时候发现值一致 但是B又去更新了 结果A有更新覆盖了B)

9.总结

原子类:操作的为变量
锁:代买块
synchronized:方法 代码块
Atomic为操作变量的原子类 AtomicArray是操作数组的原子类 其中每个元素都是原子的
Atomic
Reference是操作对象的原子类,可以用来做自旋锁
Atomic
FieldUpdater是针对有些时候需要原子操作的升级类
Adder是针对Atomic的升级 1.8后才有的 在竞争不激烈使用的是base变量做累加 而竞争激烈参与数组的方式分段累加,常用于求和
*Accumulator数学类是一个用于并行计算的类

祝君好梦!