CAS应用之JUC下的原子类

原子整数

AtomicInteger i = new AtomicInteger(0);

// 获取并自增(i = 0, 结果 i = 1, 返回 0),类似于 i++
System.out.println(i.getAndIncrement());

// 自增并获取(i = 1, 结果 i = 2, 返回 2),类似于 ++i
System.out.println(i.incrementAndGet());

// 自减并获取(i = 2, 结果 i = 1, 返回 1),类似于 --i
System.out.println(i.decrementAndGet());

// 获取并自减(i = 1, 结果 i = 0, 返回 1),类似于 i--
System.out.println(i.getAndDecrement());

// 获取并加值(i = 0, 结果 i = 5, 返回 0)
System.out.println(i.getAndAdd(5));

// 加值并获取(i = 5, 结果 i = 0, 返回 0)
System.out.println(i.addAndGet(-5));

// 获取并更新(i = 0, p 为 i 的当前值, 结果 i = -2, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.getAndUpdate(p -> p - 2));

// 更新并获取(i = -2, p 为 i 的当前值, 结果 i = 0, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.updateAndGet(p -> p + 2));

// 获取并计算(i = 0, p 为 i 的当前值, x 为参数1, 结果 i = 10, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
// getAndUpdate 如果在 lambda 中引用了外部的局部变量,要保证该局部变量是 final 的
// getAndAccumulate 可以通过 参数1 来引用外部的局部变量,但因为其不在 lambda 中因此不必是 final
System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));

// 计算并获取(i = 10, p 为 i 的当前值, x 为参数1, 结果 i = 0, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));

原子引用

AtomicReference

class DecimalAccountSafeCas implements DecimalAccount {
    AtomicReference<BigDecimal> ref;
    public DecimalAccountSafeCas(BigDecimal balance) {
        ref = new AtomicReference<>(balance);
    }
    public BigDecimal getBalance() {
        return ref.get();
    }
    @Override
    public void withdraw(BigDecimal amount) {
        while (true) {
            BigDecimal prev = ref.get();
            BigDecimal next = prev.subtract(amount);
            if (ref.compareAndSet(prev, next)) {
                break;
            }
        }
    }
}

解决ABA问题

AtomicStampedReference

AtomicStampedReference附带一个Stamp信息(版本号)

static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
public static void main(String[] args) throws InterruptedException {
    log.debug("main start...");
    // 获取值 A
    String prev = ref.getReference();
    // 获取版本号
    int stamp = ref.getStamp();
    log.debug("版本 {}", stamp);
    // 如果中间有其它线程干扰,发生了 ABA 现象
    other();
    sleep(1);
    // 尝试改为 C
    log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1));
}
private static void other() {
    new Thread(() -> {
        log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B",
        ref.getStamp(), ref.getStamp() + 1));
        log.debug("更新版本为 {}", ref.getStamp());
    }, "t1").start();
    sleep(0.5);
    new Thread(() -> {
        log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A",
        ref.getStamp(), ref.getStamp() + 1));
        log.debug("更新版本为 {}", ref.getStamp());
    }, "t2").start();
}

AtomicMarkableReference

AtomicStampedReference可以知道变量被更新了几次,但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了AtomicMarkableReference

class GarbageBag {
    String desc;
    public GarbageBag(String desc) {
        this.desc = desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    @Override
    public String toString() {
        return super.toString() + " " + desc;
    }
}

public class TestABAAtomicMarkableReference {
    public static void main(String[] args) throws InterruptedException {
        GarbageBag bag = new GarbageBag("装满了垃圾");
        // 参数2 mark 可以看作一个标记,表示垃圾袋满了
        AtomicMarkableReference<GarbageBag> ref = new AtomicMarkableReference<>(bag, true);
        log.debug("主线程 start...");
        GarbageBag prev = ref.getReference();
        log.debug(prev.toString());
        new Thread(() -> {
            log.debug("打扫卫生的线程 start...");
            bag.setDesc("空垃圾袋");
            while (!ref.compareAndSet(bag, bag, true, false)) {}
            log.debug(bag.toString());
        }).start();
        Thread.sleep(1000);
        log.debug("主线程想换一只新垃圾袋?");
        boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);
        log.debug("换了么?" + success);
        log.debug(ref.getReference().toString());
    }
}

原子数组

//获得数组第i个下标的元素  
public final int get(int i);  

//获得数组的长度  
public final int length();  

//将数组第i个下标设置为newValue,并返回旧的值  
public final int getAndSet(int i, int newValue);  

//进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true  
public final boolean compareAndSet(int i, int expect, intupdate);  

//将第i个下标的元素加1  
public final int getAndIncrement(int i);  

//将第i个下标的元素减1  
public final int getAndDecrement(int i);  

//将第i个下标的元素增加delta(delta可以是负数)  
public final int getAndAdd(int i, int delta);  

字段更新器

package automic;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
 * 原子整型字段更新操作
 * @author gosaint
 *
 */
public class AtomicIntegerFieldUpdaterTest {
     private static Class<Person> cls;
     /**
      * AtomicIntegerFieldUpdater说明
      * 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。此类用于原子数据结构,
      * 该结构中同一节点的几个字段都独立受原子更新控制。
      * 注意,此类中 compareAndSet 方法的保证弱于其他原子类中该方法的保证。
      * 因为此类不能确保所有使用的字段都适合于原子访问目的,所以对于相同更新器上的 compareAndSet 和 set 的其他调用,
      * 它仅可以保证原子性和可变语义。
      * @param args
      */
     public static void main(String[] args) {
        // 新建AtomicLongFieldUpdater对象,传递参数是“class对象”和“long类型在类中对应的名称”
        AtomicIntegerFieldUpdater<Person> mAtoLong = AtomicIntegerFieldUpdater.newUpdater(Person.class, "id");
        Person person = new Person(12345);
        mAtoLong.compareAndSet(person, 12345, 1000);
        System.out.println("id="+person.getId());
     }
}

class Person {
    volatile int id;
    public Person(int id) {
        this.id = id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
}

原子累加器

参考 https://www.jianshu.com/p/b3c5b05055de

Unsafe类

Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得

public class UnsafeAccessor {
    static Unsafe unsafe;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new Error(e);
        }
    }
    static Unsafe getUnsafe() {
        return unsafe;
    }
}

Unsafe的compareAndSwap***方法提供了cas操作

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/cas%e5%ba%94%e7%94%a8%e4%b9%8bjuc%e4%b8%8b%e7%9a%84%e5%8e%9f%e5%ad%90%e7%b1%bb/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年2月2日
下一篇 2020年2月3日

相关推荐

  • HashSet源码分析

    Set家族一览: HashSet简介 Set是Collection三大接口其中之一,意为集合,且元素不能重复。Set接口中的方法和Collection中的方法完全一致,只是起到一个…

    2019年12月2日
    0200
  • Java多线程基础

    多线程应用 异步调用 以调用方角度来讲,如果+ 需要等待结果返回,才能继续运行就是同步+ 不需要等待结果返回,就能继续运行就是异步 同步在多线程中还有另外一个意思,是让多个线程步调…

    2020年1月31日
    0340
  • Java中的四种内部类

    我发现最近真是越来越没有东西写了。。。不可能天天学习新知识啊,最近在复习阶段了,复习的东西大多数是博客里写过的/(ㄒoㄒ)/ 复习Java基础的时候认真看了一下Java的内部类,这…

    Java 2020年5月23日
    0100
  • Java8避免空指针异常Optional类的使用

    最近都是一天写一篇算法题解,好久没有写过博客了,不知道写啥了而且快到期末考试了。。 今天介绍一个Java8的特性:Optional类,这个类我平时也不咋用,今天来研究一下。 Opt…

    Java 2020年6月3日
    0660
  • 对象的输入输出-Java序列化机制

    对象序列化和反序列化,在Java中体现为两种字节流: ObjectInputStream、ObjectOutputStream 序列化的概念 指堆内存中的java对象数据,通过某种…

    Java 2019年12月21日
    0170
  • JUC包下的线程协作计数CountDownLatch及CyclicBarrier

    CountDownLatch 概述 用来进行线程同步协作,等待所有线程完成倒计时。 其中构造参数用来初始化等待计数值,await() 用来等待计数归零,countDown() 用来…

    Java 2020年2月6日
    01950
  • String s = new String("123");创建了几个String对象?

    提要 最近复习看到一道题目很有意思啊: String s = new String("123"); 问这样一行代码创建了几个String对象? 乍一看我其实以为和JDK的版本有关系…

    Java 2020年5月24日
    0580
  • Java基础查缺补漏05

    继续我的复习刷题 可以有和类名同名的函数 题目: JAVA中,下列语句哪一个正确() A. class中的constructor不可省略B. constructor必须与class…

    Java 2020年5月29日
    0140
  • 按键排序的Map-TreeMap源码分析

    总结 总结放前面(这篇挺短的)1. TreeMap基于红黑树实现,可以对Map中的key排序2. 它的排序和定位需要依赖比较器或实现 Comparable 接口,也因此不需要key…

    Java 2020年2月16日
    0130
  • NIO零拷贝与其系统函数调用

    本文参考资源: Java NIO学习笔记四(零拷贝详解)Java拿笔小星的博客-CSDN博客 关于Buffer和Channel的注意事项和细节 ByteBuffer不止可以存取by…

    2020年3月14日
    0490

发表回复

登录后才能评论