juc包下的原子类
针对基础类型地原子性读写而设计的原子类:
AtomicBoolean AtomicInteger AtomicIntegerArray AtomicIntegerFieldUpdater<T> AtomicLong AtomicLongArray AtomicLongFieldUpdater<T>
针对引用类型地原子性读写而设计的原子类:
AtomicReference<V> AtomicReferenceArray<E> AtomicReferenceFieldUpdater<T,V> AtomicMarkableReference<V> AtomicStampedReference<V>
AtomicReference
该类提供了对象引用的非阻塞原子性读写操作。
源码:
public class AtomicReference<V> implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; //volatile修饰了一个泛型的value属性 private volatile V value; }
非线程安全的代码测试:
public static void main(String[] args) { Prize prize = new Prize("小米汽车", 100); AtomicInteger atomicInteger = new AtomicInteger(); IntStream.range(0, 300).forEach( value -> { new Thread( () -> { //①获得当前还剩多少号 int count = prize.getCount(); if (count > 0) { //②对剩余号源减1,并更新回奖池 prize.setCount(count - 1); atomicInteger.incrementAndGet(); log.info("当前线程:{},抢到了 {} 号", Thread.currentThread().getName(), count); } } ).start(); } ); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } log.info("中奖人数:{}", atomicInteger.get()); }以上代码存在线程不安全。其不安全的本质是:当线程1、线程2同时执行到①处,假如都获得了当前剩余号数10,继续往下执行到②处,都对其进行了减1,最终两个线程更新回去却是9;针对这种情况我们有很多解决方案,这里我选择使用AtomicReference类进行测试: 首先定义需要引用的奖品类:
//定义我们的奖品类 @Data public class Prize { /** * 一等奖:小米汽车 */ private String level; /** * 数量 */ private int count; public Prize(String level, int count) { this.level = level; this.count = count; } }
接下来对该类创建的对象做引用类型的原子性操作:
public static void main(String[] args) { //将我们的初始奖池封装到AtomicReference中 AtomicReference<Prize> reference = new AtomicReference<>(new Prize("小米汽车", 100)); AtomicInteger atomicInteger = new AtomicInteger(0); IntStream.range(0, 300).forEach( value -> { new Thread( () -> { //①获得当前还剩多少号的对象 final Prize prize = reference.get(); if (prize.getCount() > 0) { //②对剩余号源进行减1 Prize prizeNew = new Prize(prize.getLevel(), reference.get().getCount() - 1); //③将数据更新到奖池 if (reference.compareAndSet(prize, prizeNew)) { log.info("当前线程:{},抢到了 {} 号", Thread.currentThread().getName(), prize.getCount()); atomicInteger.incrementAndGet(); } } } ).start(); } ); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } log.info("中奖人数:{}", atomicInteger.get()); }观察上面代码:虽然①②处也会出现之前基础版本的情况,但是最终③将数据刷新回奖池的时候,如果prize对象的引用已经被其他线程修改,则当前线程执行**reference.compareAndSet(prize, prizeNew)**会更新失败。对于这个线程来说,好气呀,手都伸进抽奖箱了,还是没有抢到大奖;对于老板来说无伤大雅,只管送出指定数量即可;针对这种可以搞一个while循环让线程进行重试(摸一次就可以了嘛,还想摸多少次?)
原文链接:并发编程之AtomicReference - 掘金 (juejin.cn)
标签:count,JUC,prize,原子,线程,new,操作,AtomicReference,Prize From: https://www.cnblogs.com/ryxxtd/p/17384625.html