当前位置: 首页 > 编程笔记 >

Java原子变量类常见问题解决

丁子石
2023-03-14
本文向大家介绍Java原子变量类常见问题解决,包括了Java原子变量类常见问题解决的使用技巧和注意事项,需要的朋友参考一下

在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性。于是我就写了一段代码试试,自认为非常正确。

public class Test{
  private static AtomicInteger ID = new AtomicInteger(0);
  public static int nextID(){ //返回的ID范围为 1~100
    if(ID.get() == 100) { //ID到达100时,则从1开始
      ID.set(1);
      return ID.get(); // return ID = 1;
    }
    else
      return ID.incrementAndGet(); //++ID
  }
  public static void main(String[] args) throws Exception{
    for(int i = 0; i < 5; i++){
      new Thread(()->{
        for(int j = 0; j < 100; j++)
          nextID();
      }).start();
    }
    Thread.sleep(1000); //应该输出100才对
    System.out.println(ID);
  }
}

用五个线程并发获得ID,每个线程获取100个,最后应该输出100才是,但试了好几次都不是100。原子变量类不是能保障原子性和可见性吗,为什么出现了竞态?

纠结了很久,还是很懵逼。后来发现 get 方法相当于读取一个 volatile 变量,而读取一个 volatile 变量时,不具备排他性!(AtomicInteger类内部使用了volatile修饰了value值,而volatile关键字不具备排他性)

也就是说,当一个线程刚读取到了共享的 volatile 变量的值时,其他线程可会马上对共享变量进行修改。如,线程A读取到ID的值为99时(还没对ID进行修改),其他线程可能马上就将ID加1了,此时共享变量为100了,其他线程再获取ID时,应该令ID=1才是,但线程A已经进入了else分支,它还认为ID=99,而不知道其他线程刚把ID加1变成了100,所以会吧ID加上1变成了101,这就出现了竞态。

《Java多线程编程实战指南 - 核心篇》中,作者说:“可见性的保障仅仅意味着一个线程能够读取到共享变量的相对新值,而不能保障该线程能读取到相应变量的最新值”。如volatile对可见性的保障就是保障的相对新值,由于volatile不具备排他性,所以有可能读线程刚读到一个相对新值,写线程就更改了共享变量,此时,读线程刚刚读取到的相对新值就不是最新的了。

作者对相对新值和最新值的定义:

对于同一个共享变量而言,一个线程更新了该变量的值之后,其他线程能够读取到这个更新后的值,那这个值就被称为该变量的 相对新值。

如果读取这个共享变量的线程在读取并使用该变量的时候其他线程无法更新该变量的值,那么该线程读取到的相对新值就被称为该变量的 最新值。需要加锁,才能读取到最新值。

解决办法,使用原子操作 compareAndSet:

private static int nextID(){ //返回的ID范围为 1~100
  ID.compareAndSet(100, 0);
  return ID.incrementAndGet();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 问1:INSERT/UPDATE/DELETE被解析为Query或DDL语句? 答1: 出现这类情况主要原因为收到的binlog就为Query事件,比如: binlog格式为非row模式,通过show variables like 'binlog_format'可以查看. 针对statement/mixed模式,DML语句都会是以SQL语句存在 mysql5.6+之后,在binlog为row模式下

  • 本页面罗列了大家使用 YApi 时遇到的常见问题. 如果没有找到您要的答案,请联系管理员. Q1 怎样联系组长? 组长分为 分组组长 和 项目组长: 分组组长:选择首页左侧的分组,点击右侧面板的 成员列表,成员右侧显示着 组长/开发者 的权限信息。 项目组长: 点击项目页的 设置 - 成员列表,成员右侧显示着 组长/开发者 的权限信息。 Q2 怎么快速迁移旧项目? 第一步. 使用 Chrome 浏

  • 译者:冯宝宝 我的模型报告“cuda runtime error(2): out of memory” 正如错误消息所示,您的GPU显存已耗尽。由于经常在PyTorch中处理大量数据,因此小错误会迅速导致程序耗尽所有GPU资源; 幸运的是,这些情况下的修复通常很简单。这里有一些常见点需要检查: 不要在训练循环中积累历史记录。 默认情况下,涉及需要梯度计算的变量将保留历史记录。这意味着您应该避免在计

  • 版本 Unity2018.3以上需要用最新ILRuntime。 最新的发布版本为V2.0,Unity2018以上可通过Package Manager方式安装 V1.4已过时,示例工程内的版本已过时,当前最新版本为master分支。 ILRuntime的性能到底怎么样 最新版ILRuntime在若干若干测试用例中,包括大规模数值计算的场景,均已超过Lua53版xlua,详细测试可参考 U3D Dem

  • 1.Q:导出的PPT是否支持动态图表?做的图表能否动态导入PPT或word中? A:不支持, 动态交互性图表是由代码组成,浏览器内核才能解析。图表秀有office插件,可以支持PPT中插入动态图表,可以参考帮助文档“如何在PPT中插入图表”。 2.Q:图表的颜色能否修改? A:在图表编辑工具中可以设置单个图表的颜色。在图册中图表颜色是在主题色系中,通过切换系统主题或者自定义主题就可以修改图表颜色。

  • 现象: 运行status命令,看到有send_fail的情况,是什么原因? 答: 有send_fail通常来说不是什么大问题,一般是由于客户端主动关闭连接或者客户端停止接收数据导致的数据发送失败。 send_fail有两种原因 1、调用send接口向客户端发送数据时发现客户端已经断开,则send_fail计数加1。由于是客户端主动断开的,属于正常现象,一般可以忽略。 2、客户端接收缓冲区满(一般是