Java笔记-volatile和AtomicInteger

news2024/9/29 9:33:47

目录

  • 1. volatile
    • 1.1.什么是volatile
    • 1.2.JMM-Java内存模型
  • 2 验证volatile的特性
    • 2.1 可见性
    • 2.2.验证volatile不保证原子性
    • 2.3 volatile实现禁止指令重排序
  • 3.使用AtomicInteger解决volatile的不能实现原子性的问题
    • 3.2 AtomicInteger的方法说明:
    • 3.3 CAS
    • 3.4 应用

1. volatile

1.1.什么是volatile

volatile是Java虚拟机提供的轻量级的同步机制,保证了可见性和有序性(禁止指令重排序),保证了JMM三个特性中的两个

1.2.JMM-Java内存模型


JMM的三个特性:
可见性、有序性、原子性
可见性:

线程在自己的工作内存中修改了从主内存中拷贝的共享变量副本后,并把修改后的值重新传到主内存中进行更新。这时我们要保证其他线程第一时间也可以得到共享变量已经被修改的通知。这样就保证了线程之间的一个可见性(因为线程间是不能直接访问对方的工作内存,所以可以从主内存下手)
有序性:
禁止指令重排,避免多线程的环境下,程序出现乱序执行的现象。
指令重排:计算机在执行程序时,为了提高性能,编译器和处理器常常回对指令做重排


原子性:
某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整,要么同时成功,要么同时失败。

2 验证volatile的特性

2.1 可见性

package volatileTest;

class MyData{
    volatile int num=0;
    public void addTo60(){
        this.num=70;
    }
}
public class Test1 {
    public static void main(String[] args) {
        MyData data=new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "进来了。。。");
            try {
                Thread.sleep(3);//保证主线程已经得到了num=0
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.addTo60();
            System.out.println(Thread.currentThread().getName()+"将值改为"+data.num);

        },"AAA").start();


        while (data.num==0){

        }
        System.out.println(Thread.currentThread().getName() + "近啦了");
            System.out.println(Thread.currentThread().getName()+"获取num="+data.num);
    }
}


说明:num被voalite修饰,AAA线程执行了addTo60后,将num的值改为70,如果没有可见性的话,主线程main是不会感受到num已经被修改了,应该会一直循环,但结果表明,main并没有一直在循环体中,而是可以得到70这个值,所以表明,volatile修饰了变量,使其具有可见性

2.2.验证volatile不保证原子性

package volitileTest;
/**
 * 验证volatile不保证原子性
 */
class Num{
    volatile int num=0;
    //20个线程对num进行加1操作,每个线程执行1000次,理论上应该为20000
    public void numAdd(){
        num++;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Num obj=new Num();
        for(int i=1;i<=20;i++) {
            new Thread(()->{
              for(int j=0;j<1000;j++){
                  obj.numAdd();
              }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"获取结果为:"+obj.num);
    }
}


说明:理论值应该是20000,但实际结果小于20000.
为什么不能保证原子性,因为没有锁,线程会进行争抢,不能及时将修改后的值写回主内存

2.3 volatile实现禁止指令重排序

3.使用AtomicInteger解决volatile的不能实现原子性的问题

package volitileTest;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 验证volatile不保证原子性
 * 解决不保证原子性的问题--AtomicInteger
 */
class Num{
    volatile int num=0;
    //20个线程对num进行加1操作,理论上应该为20000
    public void numAdd(){
        num++;
    }

    AtomicInteger atomicInteger=new AtomicInteger();
    public void myAtomicAdd(){
        atomicInteger.getAndIncrement();//每次加1
    }
}
public class Test2 {
    public static void main(String[] args) {
        Num obj=new Num();
        for(int i=1;i<=20;i++) {
            new Thread(()->{
              for(int j=0;j<1000;j++){
                  obj.numAdd();
                  obj.myAtomicAdd();
              }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"获取结果为:"+obj.num);
        System.out.println(Thread.currentThread().getName()+"获取结果为:"+obj.atomicInteger);
    }
}


说明:可以看到使用了AtomicInteger后,得到的结果与预期相符

3.2 AtomicInteger的方法说明:

1.incermentAndGet()—相当于++i,先加1再返回


2.getAndIncrement()–相当于i++,先返回再加1

3.相同点,内部都调用了unsafe类的getAndAddInt()方法

可以看到为什么AtomicInteger能实现原子性,因为原理是CAS

3.3 CAS

CAS=Compare and Set
CAS是指,在这个操作中,如果AtomicInteger的当前值是prev,那么就更新为一个预期值(这里预期值是当前值加1),返回true。如果AtomicInteger的当前值不是prev,就什么也不干,返回false。通过CAS操作并配合do … while循环,即使其他线程修改了AtomicInteger的值,最终的结果也是正确的。

3.4 应用

使用java.util.concurrent.atomic提供的原子操作可以简化多线程编程:

1.原子操作实现了无锁的线程安全;

2.适用于计数器,累加器等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/339216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux-进程1-进程概述

写在最前 记录一下linux的进程学习专题 1. 程序和进程的区别 1.1 程序 程序是包含一系列信息的文件&#xff0c;这些信息描述了如何在运行时创建一个进程&#xff1a; 二进制格式标识&#xff1a;每个程序文件都包含用于描述可执行文件格式的元信息。内核利用此信息来解 释文…

Redis实战-session共享之修改登录拦截器

在上一篇中Redis实战之session共享&#xff0c;我们知道了通过Redis实现session共享了&#xff0c;那么token怎么续命呢&#xff1f;怎么刷新用户呢&#xff1f;本来咱们就通过拦截器来实现这两个功能。 登录拦截器优化&#xff1a; 先来看看现在拦截器情况&#xff1a; 拦截…

JavaScipt基础学习(1)

1. JavaScript特点 JavaScript是脚本编写语言&#xff1b;所有主流浏览器都支持JavaScript&#xff1b;JavaScript基于对象语言&#xff1b;JavaScriptb变量类型是弱类型&#xff0c;没有如Java一样严格的数据类型&#xff1b;变量是弱类型的。因此定义变量时&#xff0c;只使…

WindowsServer服务器系列:部署FTP文件服务

1、点击“开始”菜单&#xff0c;选择“服务器管理器” 2、在接下来弹出页面中选择“添加角色和功能” 3、接下来点击“下一步” 4、接下来选择“基于角色或基于功能的安装”并点击“下一步” 5、选择“从服务器池中选择服务器”并点击“下一步” 6、接下来选中“Web 服务器(II…

【数模比赛】2023美国大学生数学建模比赛(思路、代码......)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

虚拟存储管理(6)

虚拟存储管理 前面介绍的存储管理方案要求作业全部装入内存才可运行。但这会出现两种情况&#xff1a; 有的作业因太大&#xff0c;内存装不下而无法运行。系统中作业数太多&#xff0c;因系统容量有限只能让少数作业先运行。 1 局部性原理 定义&#xff1a; 程序执行时&a…

TCP网络编程中connect()、listen()和accept()三者之间的关系

基于 TCP 的网络编程开发分为服务器端和客户端两部分&#xff0c;常见的核心步骤和流程如下&#xff1a; connect()函数 对于客户端的 connect() 函数&#xff0c;该函数的功能为客户端主动连接服务器&#xff0c;建立连接是通过三次握手&#xff0c;而这个连接的过程是由内核…

LeetCode题目笔记——24. 两两交换链表中的节点

文章目录题目描述题目链接题目难度——中等方法一&#xff1a;迭代代码/C代码/python方法二&#xff1a;递归代码/C总结题目描述 或许这也是个经典的面试题&#xff0c;记录一手 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在…

模电学习6. 常用的三极管放大电路

模电学习6. 常用的三极管放大电路一、判断三极管的工作状态1. 正偏与反偏的概念2. 工作状态的简单判断二、三种重要的放大电路1. 共射电路2. 共集电极放大电路3. 共基极放大电路一、判断三极管的工作状态 1. 正偏与反偏的概念 晶体管分P区和N区&#xff0c; 当P区电压大于N区…

[设计模式] 建造者模式

文章目录什么是建造者模式建造者模式建造者模式中的角色UML类图代码实现建造者模式与工厂模式的区别什么是建造者模式 建造者模式(Builder Pattern)是一种创建型的设计模式&#xff0c;它将一个复杂对象的构建与它的表示分离&#xff0c;也就是复杂的构建隐藏起来&#xff0c;…

即时通讯系列-4-如何设计写扩散下的同步协议方案

1. 背景信息 上篇提到了, IM协议层是主要解决会话和消息的同步, 在实现上, 以推模式为主, 拉模式为辅. 本文Agenda: (How)如何同步(How)如何设计同步位点如何设计 Gap过大(SyncGapOverflow) 机制如何设计Ack机制总结 提示: 本系列文章不会单纯的给出结论, 希望能够分享的是&…

SpringCloud-Netflix学习笔记13——Zuul路由网关

什么是Zuul? Zuul包含了对请求的路由和过滤两个最主要的功能。 其中路由功能负责将外部请求转发到具体的微服务实例上&#xff0c;是实现外部访问统一入口的基础&#xff0c;而过滤器功能则负责对请求的处理过程进行干预&#xff0c;是实现请求校验&#xff0c;服务聚合等功能…

最详细教你注册 ChatGPT,不会来找我

超强人工智能 ChatGPT 震撼来袭&#xff0c;它是美国人工智能研究实验室 OpenAI 新推出的一种自然语言处理工具&#xff0c;不想来体验一下嘛&#xff01;最详细教程手把手教你注册&#xff0c;不会来找我&#xff01; 准备工作 一个可以科学上网的工具&#xff0c;提供非 Ch…

文献阅读笔记 # CodeBERT: A Pre-Trained Model for Programming and Natural Languages

《CodeBERT: A Pre-Trained Model for Programming and Natural Languages》EMNLP 2020 (CCF-B)作者主要是来自哈工大、中山大学的 MSRA 实习生和 MSRA、哈工大的研究员。资源&#xff1a;code | pdf相关资源&#xff1a;RoBERTa-base | CodeNN词汇&#xff1a; bimodal: 双模态…

嵌入式设备搭建NFS环境(服务器/客户端、源码下载编译、文件系统适配、内核适配)

1、什么是nfs (1)NFS(Network File System)是网络文件系统&#xff0c;能让使用者访问网络上别处的文件就像在使用自己的计算机一样&#xff1b; (2)NFS是基于UDP/IP协议的应用&#xff0c;其实现主要是采用远程过程调用RPC机制&#xff0c;RPC提供了一组与机器、操作系统以及低…

CAS详解.

CAS这个机制就给实现线程安全版本的代码&#xff0c;提供了一个新的思路&#xff0c;之前通过加锁&#xff0c;把多个指令打包成整体&#xff0c;来实现线程安全。现在就可以考虑直接基与CAS来实现一些修改操作&#xff0c;也能保证线程安全&#xff08;不需要加锁&#xff09;…

OpenAi-chatgpt注册保姆级全网最详细注册教程2023年2月最新-

废话就不多说了&#xff0c;说多了浪费各位师傅的时间&#xff01;直接冲&#xff0c;在开始之前需要科学上网&#xff0c;就没其他要求了 1、访问https://chat.openai.com/auth/login 2、点击sign up,输入账号密码&#xff0c;点击Continue 3、之后会来到登陆页面&#xff0…

Oracle Dataguard(主库为 Oracle rac 集群)配置教程(03)—— 创建 dataguard 数据库之前的准备工作

Oracle Dataguard&#xff08;主库为 Oracle rac 集群&#xff09;配置教程&#xff08;03&#xff09;—— 创建 dataguard 数据库之前的准备工作 / 本专栏详细讲解 Oracle Dataguard&#xff08;Oracle 版本为11g&#xff0c;主库为双节点 Oracle rac 集群&#xff09;的配置…

云计算|OpenStack|错误记录和解决方案(不定时更新)

前言&#xff1a; openstack的部署和使用是难度比较大的&#xff0c;难免会出现各种各样的问题&#xff0c;因此&#xff0c;本文将把一些在部署和使用openstack社区版时出现的错误做一个记录&#xff0c;并就每一个错误分析和解决问题。&#xff08;尽量记录比较经典的错误&a…

微搭低代码从入门到精通10-tab栏组件

在小程序中&#xff0c;如果你的页面是由多个组成的&#xff0c;往往涉及到页面切换的问题。那如何引导用户访问不同的页面呢&#xff1f;微搭中提供了tab栏组件来实现这个功能&#xff0c;本篇我们介绍一下这个组件的使用方法。 首先呢打开我们的应用编辑器&#xff0c;在左侧…