Atomic operation tools

Atomic operation tools

1. Test the efficiency of synchronize mutex locks and other atomic tool classes in modifying attributes

package com.bilibili.juc.atomics;


import lombok.Data;

import javax.lang.model.element.VariableElement;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 测试synchronize和其他Long类型的原子操作工具类的效率
 */
class ClickNumber //资源类
{
    
    

    int number = 0;

    public synchronized void clickBySynchronized() {
    
    
        number++;
    }
    //AtomicLong是采用的CAS执行的原子操作,在低并发效率还可以,高并发不行
    AtomicLong atomicLong = new AtomicLong(0);

    public void clickByAtomicLong() {
    
    
        atomicLong.getAndIncrement();
    }
    //采用了一种分段的方式来处理原子操作。它将内部的数值分成多个段(Cells),每个段都有自己的原子计数。不同线程对不同的段进行增加操作,
    // 减少了竞争,从而提高了性能。
    LongAdder longAdder = new LongAdder();

    public void clickByLongAdder() {
    
    
        longAdder.increment();
    }

    //定义一个二元操作函数 ,x是当前值,y是accumulate()的值,初始值为0   0+1,1+1,2+1,3+1
    LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);

    public void clickByLongAccumulator() {
    
    
        longAccumulator.accumulate(1);
    }

}

/**
 * 需求: 50个线程,每个线程100W次,总点赞数出来
 */
public class AccumulatorCompareDemo {
    
    
    public static final int _1W = 10000;
    public static final int threadNumber = 50;

    public static void main(String[] args) throws InterruptedException {
    
    
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;

        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        startTime = System.currentTimeMillis();
        //50个线程,每个线程累加100万次
        for (int i = 1; i <= threadNumber; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <= 100 * _1W; j++) {
    
    
                        clickNumber.clickBySynchronized();
                    }
                } finally {
    
    
                    //每完成一次计数器减一,为0时停止阻塞
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickBySynchronized: " + clickNumber.number);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <= 100 * _1W; j++) {
    
    
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
    
    
                    //每完成一次计数器减一,为0时停止阻塞
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByAtomicLong: " + clickNumber.atomicLong.get());


        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <= 100 * _1W; j++) {
    
    
                        clickNumber.clickByLongAdder();
                    }
                } finally {
    
    
                    //每完成一次计数器减一,为0时停止阻塞
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByLongAdder: " + clickNumber.longAdder.sum());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <= 100 * _1W; j++) {
    
    
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
    
    
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByLongAccumulator: " + clickNumber.longAccumulator.get());

    }
}

Running results----costTime: 2559 milliseconds clickBySynchronized: 50000000
----costTime: 866 milliseconds clickByAtomicLong: 50000000 ----
costTime: 91 milliseconds clickByLongAdder: 50000000
----costTime: 55 milliseconds clickByLongAccumulator: 50000000

Process finished with exit code 0

2. Integer array atomic operation tool class operates to modify the array

package com.bilibili.juc.atomics;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * 整型数组原子操作工具类操作修改数组
 */
public class AtomicIntegerArrayDemo
{
    
    
    public static void main(String[] args)
    {
    
    
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
        //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
        //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});

        for (int i = 0; i <atomicIntegerArray.length(); i++) {
    
    
            System.out.println(atomicIntegerArray.get(i));
        }

        System.out.println();

        int tmpInt = 0;


         //给指定的索引设置新值
        tmpInt = atomicIntegerArray.getAndSet(0,1122);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));

        //给自定索引的数值加一
        tmpInt = atomicIntegerArray.getAndIncrement(0);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
    }
}

operation result:

0
0
0
0
0

0 1122
1122 1123

Process finished with exit code 0

3. Integer atomic operation tools

package com.bilibili.juc.atomics;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 整型原子操作工具类
 */
class MyNumber
{
    
    
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addPlusPlus()
    {
    
    
        atomicInteger.getAndIncrement();
    }
}



public class AtomicIntegerDemo
{
    
    
    public static final int SIZE = 50;

    public static void main(String[] args) throws InterruptedException
    {
    
    
        MyNumber myNumber = new MyNumber();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        //50个线程,每个线程自增1000次,最后拿到打印结果50000
        for (int i = 1; i <=SIZE; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <=1000; j++) {
    
    
                        myNumber.addPlusPlus();
                    }
                } finally {
    
    
                    //每个线程执行完就会减一,计数器减到0就会停止阻塞
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        //等待上面50个线程全部计算完成后,再去获得最终值

        //暂停几秒钟线程
        //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        //计数器减到0就会停止阻塞
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get());
    }
}

Running result: main result: 50000

4. Atomic integer field updater

Used to operate integer fields in classes

package com.bilibili.juc.atomics;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * AtomicIntegerFieldUpdate 对类中的指定整型类型字段进行 原子自增操作
 */
class BankAccount//资源类
{
    
    
    String bankName = "CCB";

    //更新的对象属性必须使用 public volatile 修饰符。  JMM三大特性 原子性,可见性,有序性  ;volatile 不能保证原子性
    public volatile int money = 0;//钱数

    public void add()
    {
    
    
        money++;
    }

    //因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");

    //不加synchronized,保证高性能原子性,局部微创小手术
    public void transMoney(BankAccount bankAccount)
    {
    
    
        fieldUpdater.getAndIncrement(bankAccount);
    }


}

/**
 * 以一种线程安全的方式操作非线程安全对象的某些字段。
 *
 * 需求:
 * 10个线程,
 * 每个线程转账1000,
 * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现。
 */
public class AtomicIntegerFieldUpdaterDemo
{
    
    
    public static void main(String[] args) throws InterruptedException
    {
    
    
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 1; i <=10; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    for (int j = 1; j <=1000; j++) {
    
    
                        //bankAccount.add();
                        bankAccount.transMoney(bankAccount);
                    }
                } finally {
    
    
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }

        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+"\t"+"result: "+bankAccount.money);
    }
}

Running result: main result: 10000

5. Atomic reference field updater

package com.bilibili.juc.atomics;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * 原子引用字段更新器
 */
class MyVar //资源类
{
    
    
    public volatile Boolean isInit = Boolean.FALSE;

    //把指定isInit的操作变为原子操作
    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");

    public void init(MyVar myVar)
    {
    
    
        if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
        {
    
    
            System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds");
            //暂停几秒钟线程
            try {
    
     TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t"+"----- over init");
        }else{
    
    
            System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。");
        }
    }
}


/**
 * 需求:
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
 * 要求只能被初始化一次,只有一个线程操作成功
 */
public class AtomicReferenceFieldUpdaterDemo
{
    
    
    public static void main(String[] args)
    {
    
    
        MyVar myVar = new MyVar();

        for (int i = 1; i <=5; i++) {
    
    
            new Thread(() -> {
    
    
                myVar.init(myVar);
            },String.valueOf(i)).start();
        }
    }
}

operation result:

1 ----- start init, need 2 seconds
2 ----- There is already a thread performing initialization work. . . . .
4 ----- There is already a thread performing initialization work. . . . .
3 ----- There is already a thread performing initialization work. . . . .
5 ----- There is already a thread performing initialization work. . . . .
1 ----- over init

6. Mark reference tool class

Use it to implement CAS (compare and swap)

package com.bilibili.juc.atomics;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;

/**
标记引用工具类用来实现CAS
 */
public class AtomicMarkableReferenceDemo
{
    
    
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);

    public static void main(String[] args)
    {
    
    
        new Thread(() -> {
    
    
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);
            //暂停1秒钟线程(这就是为什么t2也是false),等待后面的T2线程和我拿到一样的模式flag标识,都是false
            try {
    
     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
            //拿到期望的标识false ,把markableReference的标识设置为true
            markableReference.compareAndSet(100,1000,marked,!marked);
            boolean t1marked = markableReference.isMarked();
            System.out.println("t1修改后的mark:"+t1marked);
        },"t1").start();

        new Thread(() -> {
    
    
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);

            try {
    
     TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
            boolean beforeT2Update = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"修改前的标识:"+marked);
            //如果 markableReference 中的引用是 100 且标志位的状态与 marked 变量相同,那么它将把引用更改为 2000,并将标志位的状态取反。
            boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
            System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CAS修改result: "+b);
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
        },"t2").start();
    }
}

/**
 *  CAS----Unsafe----do while+ABA---AtomicStampedReference,AtomicMarkableReference
 *
 *  AtomicStampedReference,version号,+1;
 *
 *  AtomicMarkableReference,一次,false,true
 *
 */

operation result:

t1 Default mark: false
t2 Default mark: false
t1 modified mark: true
t2 Mark before modification: false
t2 t2 thread CAS modified result: false
t2 true
t2 1000

Process finished with exit code 0

7. Long type atomic operation class and custom atomic binary operation function tool class

package com.bilibili.juc.atomics;

import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;

/**
  Long类型原子操作类 采用了一种分段的方式来处理原子操作。它将内部的数值分成多个段(Cells),每个段都有自己的原子计数。不同线程对不同的段进行增加操作,
 减少了竞争,从而提高了性能。
 自定义原子二元操作函数工具类:LongAccumulator
 */
public class LongAdderAPIDemo
{
    
    
    public static void main(String[] args)
    {
    
    
        LongAdder longAdder = new LongAdder();

        longAdder.increment();
        longAdder.increment();
        longAdder.increment();

        System.out.println(longAdder.sum());

        //自定义原子二元操作函数工具类
        LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator()
        {
    
    
            @Override
            public long applyAsLong(long left, long right)
            {
    
    
                return left + right;
            }
        },0);

        longAccumulator.accumulate(1);//1
        longAccumulator.accumulate(3);//4

        System.out.println(longAccumulator.get());
    }
}

operation result:

3

4

Okay, the above are some commonly used atomic operation tool classes.

Guess you like

Origin blog.csdn.net/qq_45925197/article/details/132878681