首页 > 其他分享 >【JUC】原子操作

【JUC】原子操作

时间:2023-05-09 13:45:12浏览次数:51  
标签:count JUC prize 原子 线程 new 操作 AtomicReference Prize

 

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

相关文章

  • python操作redis集群
    1基础环境分析redis版本:redis-5.0.14 2脚本示例说明:向redis集群里面写入10000000条数据,再查询下这些数据#!/usr/bin/python3importrandomimportstringimporthashlibfromredisclusterimportRedisClusterimportdatetime"""使用redis的方式向redis集群中写入......
  • jquery select 操作
    //jQuery获取Select选择的Text和Value:varcheckText=jQuery("#select_id").find("option:selected").text();//获取Select选择的TextvarcheckValue=jQuery("#select_id").val();//获取Select选择的optionValuevarcheckIndex=jQuery("#sel......
  • SAP HANA 基本操作
    su-hdbadm--创建租户数据库hdbsql-uSYSTEM-p'P@ssw0rd'-i00-dSYSTEMDB'CREATEDATABASETESTSYSTEMUSERPASSWORD"P@ssw0rd"'--备份系统库以及租户库hdbsql-dSYSTEMDB-uSYSTEM-p'P@ssw0rd'-i00"BACKUPDATAUSINGFIL......
  • Java操作TIFF
    一般Java通过javax.imageio.ImageIO操作jpeg与png没有问题,但是操作tiff时就会报错,此时只要引入如下的包即可,不需要修改代码即可兼容tiff<dependency><groupId>com.twelvemonkeys.imageio</groupId><artifactId>imageio-core</artifactId></dependency>......
  • Linux操作系统优化
    第18章Linux操作系统优化目录第18章Linux操作系统优化1.更改Yum源和添加epel源2.关闭SELinux3.关闭防火墙(Firewalld)4.关闭NetworkManager5.同步系统时间6.加大文件描述7.别名及环境变量优化8.内核优化9.配置SSH远程管理服务10.修改主机名和IP脚本11.安装常用软件12.......
  • 树的基本操作
    classTreeNode{val:numberleft:TreeNode|nullright:TreeNode|nullconstructor(val?:number,left?:TreeNode|null,right?:TreeNode|null){this.val=val===undefined?0:valthis.left=left===undefined?null:left......
  • git简明操作教程
    我发现很多人依然对git无所适从。那我就写个简明操作教程来供还在学习路上的同学参考。git工作原理:分布式,每个克隆或更新远程仓库的用户都拥有⼀一份最新的完整的仓库。分支结构,不要直接操作remote的远程分支,应该首先checkout⼀一份到本地分支。远程分支是指与远程仓库上同步......
  • Golang MySQL 操作
    1.  创建go_db目录      mkdirgo_db2. root@VirtualBox:/mnt/share/goframe/go_db#gomodinitgo_dbgo:creatingnewgo.mod:modulego_dbroot@VirtualBox:/mnt/share/goframe/go_db#goget-ugithub.com/go-sql-driver/mysqlgo:addedgithub.com/go-......
  • 关于使用kubeadm reset命令对kubeadm init与kubeadm join操作后遇到报错的情况-进行恢
    在Kubernetes/k8s集群中,无论是在开始的master节点初始化,还是后面客户端使用kubeadmjoin命令加入到集群可能都会遇到很多报错,对于新手来说、很多还不是很好解决、也有一些情况,是在初始化之前忘记执行一些操作,而导致报错这种一般都需要执行漏掉的操作,重新执行初始化操作,但是如......
  • 项目创始者和开发者操作
    1.电脑指定位置拉取代码: 2.进入拉取文件夹输入gitlog即可查看之前的操作日志: 3.截至提交到版本库的流程: 4.提交到远程仓库: 5.从远程仓库拉取代码: 6.c.txt文件中修改内容,提交到远程仓库: 7.协同开发: 8.总结: ......