java基本数据类型的原子操作类

保证原子性的基本数据类型

在import java.util.concurrent.atomic包下提供了一些对于基本数据类型操作的具有原子性的类:

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

下面以AtomicInteger为例进行讲解

AtomicInteger简介

java.util.concurrent.atomic.AtomicInteger类是可以保证原子性的,基于了cas实现,因此是线程安全。我们之前对int类型计算的时候并不是原子性的,在多线程环境下是会有问题,需要加锁解决,倘若不想加锁的话,可以使用AtomicInteger来解决线程安全的问题。

下面介绍一些AtomicInteger中的方法,这些方法里面调用了sun.misc.Unsafe中的方法

public class Test {

    public static void main(String[] args) {
        //修改ai的操作具有原子性
        AtomicInteger ai = new AtomicInteger(8);

        //修改i的操作不具有原子性,比如i++等
        int i = 8;

        //下面操作都是具有原子性的
        System.out.println(ai.incrementAndGet());//相当于++i
        System.out.println(ai.getAndIncrement());//相当于i++
        System.out.println(ai.decrementAndGet());//相当于--i
        System.out.println(ai.getAndDecrement());//相当于i--
        System.out.println(ai.get());//获取当前ai的值
        System.out.println(ai.getAndAdd(6));//先获取ai的值,然后在加6
        System.out.println(ai.addAndGet(6));//先加6,然后再获取ai的值
        System.out.println(ai.updateAndGet(value -> value * 6));//相当于value = value * 6,做乘法
        System.out.println(ai.getAndUpdate(value -> value * 6));//先获取value的值,然后再做乘法

    }
}

使用AtomicInteger实现多线程卖票

之前我们使用了synchronized和ReentrantLock解决了多线程卖票的问题,这里再使用AtomicInteger来实现一下,由于AtomicInteger具有原子性,所以不用再加锁了。

代码:

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 练习:使用多线程程序模拟电影票售卖的过程,一共有100张电影票,3个公司对外售卖
 * 分析:将100张电影票作为成员变量,开启三个线程开始执行,每当线程执行一次,票的总数减一
 */
public class Ticket implements Runnable {

    //100张电影票
    private AtomicInteger count = new AtomicInteger(100);

    @Override
    public void run() {
        while (true){
            //如果剩余0张则跳出死循环
            int surplusCount = 0;
            //count.decrementAndGet()总票数自减1
            if ((surplusCount = count.decrementAndGet()) < 0) {
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖出一张,剩余" + surplusCount + "张");
            }
        }
    }

}

测试类:

public class Test {

    public static void main(String[] args) {
        Ticket t = new Ticket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);

        t1.setName("猫眼电影");
        t2.setName("糯米电影");
        t3.setName("美团电影");

        t1.start();
        t2.start();
        t3.start();

    }
}

LongAdder的使用

在jdk8中新增了LongAdder,效率比AtomicLong高,但是会耗费更多的空间。下面通过案例演示LongAdder的使用。

import java.util.concurrent.atomic.LongAdder;


public class Ticket implements Runnable {

    //从0开始
    private LongAdder count = new LongAdder();

    @Override
    public void run() {
        while (true){
            //获取当前的值
            int total = count.intValue();
            //如果到了100则退出循环
            if (total == 100) {
                break;
            }else {
                //增长1
                count.increment();
                System.out.println(Thread.currentThread().getName() + "卖出一张:" + count.intValue() + "张");
            }
        }
    }

}

测试类:

public class Test {

    public static void main(String[] args) {
        Ticket t = new Ticket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);

        t1.setName("猫眼电影");
        t2.setName("糯米电影");
        t3.setName("美团电影");

        t1.start();
        t2.start();
        t3.start();
    }
}

LongAdder分析

AtomicLong会操作一个变量进行累加操作,多个线程操作一个变量,势必会影响性能。LongAdder则是设置了多个累加单元,多个线程分别操作这些累加单元,最终进行汇总操作,从而提升了性能。