JUC-JMM/Volatile/单例模式

2023-05-16

JMM

Java内存模型,是一个概念,不存在的东西,概念、约定

关于JMM的一些同步约定:
  1. 线程解锁前,必须把共享变量刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存中
  3. 加锁和解锁是同一把锁
内存交互八种操作

  • lock
  • unlock
  • read
  • load
  • use
  • assign
  • store
  • write

规则:

  • 不允许read和load、store和write单独出现
  • 不允许线程丢弃最近的assign操作,工作变量改变,必须告诉主存
  • 一个线程将没有assign的数据同步回主存
  • 一个新的变量必须从主存中诞生,不允许工作内存直接使用一个未初始化的变量。
  • 一个变量同一时间只有一个线程对其进行lock,必须执行相同次数的unlock
  • 对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎是由这个变量前,必须重新load或assign操作初始化变量的值
  • 对一个变量进行unlock操作之前,必须把变量同步回主内存。

Volatile

是Java虚拟机提供的轻量级的同步机制

  • 保证可见性,线程A对主存中变量的修改,线程B会知道,给变量加上volatile

  • 不保证原子性,原子性是不可分割。

    /*private static volatile int num = 0;// volatile 不保证原子性的
    	public static void add(){
       		num++;// 不具有原子性
    	}
    */
    //java.util.concurrent.atomic.AtomicInteger;
    private static volatile AtomicInteger num =new AtomicInteger();
    public static void add(){
        num.getAndIncrement(); //用的是CAS,效率比synchronized高
    }
    
    public static void main(String[] args) {
        // 按道理结果应该是20000,但是
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " "+num);
    }
    

    如果不加lock和synchronized,怎么保证原子性,java.util.concurrent.atomic

    //java.util.concurrent.atomic.AtomicInteger;
    private static volatile AtomicInteger num =new AtomicInteger();
    public static void add(){
        num.getAndIncrement(); //用的是CAS,效率比synchronized高, 在内存中修改值,UnSafe类是特殊的存在
    }
    
  • 禁止指令重排

    • 指令重排,计算机并不是按照写的程序顺序执行的。
    • 源代码->编译器优化的重排->指令并行可能重排->内存系统也会重排->执行
    • 指令重排会考虑数据之间的依赖性的。

    a,b,x,y=0

    线程A线程B
    x=ay=b
    b=1a=2

    正常结果:x=0,y=0

    线程A线程B
    b=1a=2
    x=ay=b

    指令重排之后:x=2, y=1

    Volatile可以避免指令重排:

    内存屏障(禁止上面和下面指令顺序交换),CPU指令:

    • 保证特定的操作的执行顺序
    • 可以保证某些变量的内存可见性,利用这些特性,volatile实现了可见性

单例模式

  • 饿汉式

    package com.clay.single;
    // 饿汉式单例
    public class Hungry {
    
        private Hungry(){
    
        }
        //饿汉式,一上来就加载了一个对象,可能会浪费空间
        private final static Hungry HUNGRY = new Hungry();
        public static Hungry getInstance(){
            return HUNGRY;
        }
        public static void main(String[] args) {
    
        }
    }
    
    
  • 懒汉式

    package com.clay.single;
    
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    // 懒汉式单例
    public class LazyMan {
    
        private static boolean flag = false;
        private LazyMan(){
    //        synchronized (LazyMan.class){
    //            // 防止通过反射来破坏单例,但是
    //            // 多个反射同时创建还是会出问题
    //            if(lazyMan != null){
    //                throw new RuntimeException("不要试图通过反射来破坏");
    //            }
    //        }
    
            synchronized (LazyMan.class){
                if(flag == false){
                    flag = true;
                }else {
                    throw new RuntimeException("不要试图通过反射来破坏");
                }
            }
        }
    
        private volatile static LazyMan lazyMan;
    
        public static LazyMan getInstance(){
            if (lazyMan == null){
                synchronized (LazyMan.class){
                    if(lazyMan==null){
                        lazyMan = new LazyMan();//不是原子性操作
                        // 分配内存空间、执行构造方法初始化对象、对象指向空间
                    // 如果发生指令重排,先执行了对象指向空间,有可能别的线程进来了
                        //判断不等于null,就直接返回,空指针,因此应加上volatile
                    }
                }
            }//并发下有问题,加双重锁
    
            return lazyMan;
        }
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            LazyMan instance = LazyMan.getInstance();
    
            //通过反射来破坏单例
            Constructor<LazyMan> constructor = LazyMan.class.getConstructor(null);
            constructor.setAccessible(true);//无视私有构造器
            LazyMan lazyMan = constructor.newInstance();
    
        }
    }
    
    
  • 静态内部类

    package com.clay.single;
    
    public class Holder {
    
        private Holder(){
    
        }
        public static Holder getInstance(){
            return InnerClass.HOLDER;
        }
        //静态内部类
        public static class InnerClass{
            private static final Holder HOLDER = new Holder();
        }
    }
    
    
  • 枚举

    反射无法破坏枚举的单例

CAS比较并交换

比较当前工作内存中的值和主内存的值,如果这个值是期望的,则执行操作,如果不是则继续循环(自旋锁),CAS是CPU原语,速度很快。

缺点:循环会耗时、一次性只能保证一个共享变量的原子性、存在ABA问题。

compareAndSet源码:(compareAndSwap)

//Atomically sets the value to the given updated value if the current value == the expected value
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//CAS   compareAndSet比较并交换
public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(2021);
    // 如果期望值相同,更新
    atomicInteger.compareAndSet(2021,2022);
    System.out.println(atomicInteger.get());
}

ABA问题:A换成B又换成A

原子引用(带版本号的Atomic),解决ABA问题

AtomicReference 和AtomicStampReference

乐观锁原理,每一次改变操作,版本号加1

各种锁

  1. 公平锁和非公平锁

    • 公平锁:不能插队,线程先来后到
    • 非公平锁:可以插队,默认一般都是非公平的
    public ReentrantLock() {
        sync = new NonfairSync();
    }//默认是非公平锁
    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }//可以更改为公平锁
    
  2. 可重入锁

    可重入锁(递归锁):拿到了外层的锁,自动也就获取了内部的锁

    class Phone{    Lock lock = new ReentrantLock();    //有两把锁,获取了我外层锁,就获得了内部锁,一因此会一个线程先执行完两个函数,再换下一个线程执行    public  void send(){        lock.lock();        try {            System.out.println(Thread.currentThread().getName() + "hi");            call();        } catch (Exception e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public  void call(){        lock.lock();        try {            System.out.println(Thread.currentThread().getName() + "call");        } catch (Exception e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }}
    
  3. 自旋锁

    不断尝试,直到成功,是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

    public class SpinLock {
        //原子引用
        AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
        // 加锁
        public void myLock(){
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"mylock");
            // 自旋锁
            while (!atomicReference.compareAndSet(null,thread)){
    
            }
        }
    	//解锁
        public void unMyLock(){
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"unmylock");
            atomicReference.compareAndSet(thread,null);
        }
    }
    
    
  4. 死锁

    两个线程互相抢夺对方手里的资源

    死锁长生的四个必要条件:

    • 互斥
    • 占有且等待:该进程本身占有资源,且同时还有资源未得到满足,正在等待其他进程释放该资源。
    • 不可抢占:不能抢占对方占有的你的资源
    • 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需要的只要一种资源
    package com.clay.lock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    
    //死锁
    public class DeadLock {
    
        public static void main(String[] args) {
            String LockA = "LockA";
            String LockB = "LockB";
            new Thread(new MyThread(LockA,LockB)).start();
            new Thread(new MyThread(LockB,LockA)).start();
    
        }
    }
    
    
    class MyThread implements Runnable{
    
        private String LockA;
        private String LockB;
    
        public MyThread(String lockA, String lockB) {
            LockA = lockA;
            LockB = lockB;
        }
    
        @Override
        public void run() {
            synchronized (LockA){
                System.out.println(Thread.currentThread().getName()+"拿到"+LockA+"想要"+LockB);
    
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (LockB){
                    System.out.println(Thread.currentThread().getName()+"拿到"+LockB+"想要"+LockA);
                }
            }
        }
    }
    

    通过jps -l定位进程号

    通过jstak 进程号来找到死锁。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUC-JMM/Volatile/单例模式 的相关文章

  • 【supervisor】安装和使用,超级详细,开机自启动,以及其它的注意细节

    1 安装supervisor yum pip的方式都可以 yum y install supervisor 已加载插件 xff1a fastestmirror Loading mirror speeds from cached hostfi
  • poj1664-放苹果

    放苹果 把N个同样的苹果放在M个同样的盘子里 xff0c 允许有的盘子空着不放 xff0c 问共有多少种不同的分法 xff1f xff08 用K表示 xff09 5 xff0c 1 xff0c 1和1 xff0c 5 xff0c 1 是同一
  • VS报错——无法打开文件XXX.lib

    添加lib文件 xff1a 配置属性 gt 链接器 gt 输入 gt 附加依赖项加入库名 xff08 lib xff09 xff1b 或者是在cpp源文件中用 pragma comment lib lib 来代替 此时编译会提示 xff1a
  • Kafka与Flink结合使用

    Kafka与Flink结合使用 本地连接kafka 首先可以先以本地模式来对kafka进行操作 当我们在系统 xff08 可以是windows xff0c 尽量linux xff09 上部署好了Zookeeper和kafka集群 xff0c
  • 当前脑电入门

    资料 1 公众号脑机接口社区汇总 2 MNE 3 EEG科普 4 公众号BrainTechnology 5 公众号路同学 6 知乎Alex 7 程序员大本营 xff0c 很多相关 数据集 上海交大SEED数据集 BCI EEG数据集 xff
  • 使用Diskspd测试SQL Server存储子系统

    In this article we will learn how to test our storage subsystems performance using Diskspd The storage subsystem is one
  • Python读取mat文件——matlab的数据

    Python处理mat文件 1 读取2 数据格式 1 读取 很多人使用MATLAB并将数据存储在 mat文件中 这些文件是什么 这些文件包含MATLAB工作空间分配给它们的变量和对象列表 它作为字典导入到Python中 xff0c 其中键是
  • 我的毕设4.24—

    EEG 1 寻找数据2 读入数据3 了解数据3 处理数据 1 寻找数据 要找到关于情感的EEG数据集 by CSDN 2 读入数据 因为获取的数据集是mat文件 xff0c 解决Python读取mat文件 by 文本检索图书馆w pytho
  • 关于提高信息传输率三个方法之一——增加频带宽度

    转载原作 以下解释为什么增加频带宽度可减弱码间串扰 主要由于高频信号分量对波形影响是变得接近数字信号 xff0c 分界垂直 xff0c 减小斜坡 信道极限容量 任何实际的信道都不是理想的 xff0c 在传输信号时会产生各种失真以及带来多种干
  • CSMA/CD计算机网络(第七版)谢希仁 3-25 CSMA/CD

    计算机网络 xff08 第七版 xff09 谢希仁 3 25 假定站点A和B在同一个10Mb s以太网网段上 这两个站点之间的传播时延为225比特时间 站点A和B在t 61 0时同时发送了数据帧 当t 61 225比特时间 xff0c A和
  • L3-019 代码排版 (30 分) 别看 没改完

    太复杂了啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 不做了 include lt iostream gt include lt cstdio gt using namespace std string s bo
  • 无人机航线规划

    非最短非最长 xff0c 因为只有10个点 xff0c 直接搜 标题 xff1a 无人机航线规划 某海岛遭遇飓风灾害 xff0c 道路设施受损严重 救援部门为尽快探明主要交通干道的受损情况 xff0c 在地图上划定了主要交通干道的侦察点 x
  • 17蓝桥C语言B组 7.日期问题

    标题 xff1a 日期问题 小明正在整理一批历史文献 这些历史文献中出现了很多日期 小明知道这些日期都在1960年1月1日至2059年12月31日 令小明头疼的是 xff0c 这些日期采用的格式非常不统一 xff0c 有采用年 月 日的 x
  • 安装Scipy。。。。。。。。

    放弃pip转用conda戳 2019 4 21 安装Scipy 以下所有安装过程假设电脑中安装有pip xff0c 如果没有就请装一个 首先 xff0c windows正常通过pip命令安装numpy和scipy的时候 xff1a pip
  • 《Python编程:从入门到实践》文件资料下载

    感谢分享者 下载地址 往下翻 下载压缩包
  • 01、BUCK电路的参数计算

    案例 xff1a 设计一个Buck电路 xff0c 满足如下性能指标要求 xff1a 一 性能指标要求 1 输入电压 2 输出电压 3 输出电压纹波 4 电流纹波 5 开关频率 二 需要计算的参数 三 BUCK电路拓扑 四 BUCK电路工作
  • mysql分页是物理分页_学习MySQL:什么是分页

    mysql分页是物理分页 In this article I am going to explain that in MySQL what is pagination and how we can implement it When we
  • Ubuntu下嵌入式arm板Qt环境搭建

    参考 xff1a https blog csdn net lpp0900320123 article details 24876159 系统环境 xff1a 主机操作系统 xff1a ubuntu14 04 主机编译器 xff1a gcc4
  • FreeRDP在windows上的编译和使用

    FreeRDP 是 Windows下远程桌面协议 xff08 RDP xff09 的一种实现 xff0c 类似于微软自带的mstsc xff0c 可以实现windows上的远程连接 xff0c 特别是对于在ubuntu或者在Mac下 xff
  • Ubuntu-GPU

    查看CUDA的版本 xff1a nvcc V 显示GPU的信息 xff1a nvidia smi 查看GPU的运行 xff1a xff08 每 0 1s 显示一次显存的情况 xff09 watch n 0 1 nvidia smi

随机推荐

  • 面向对象程序设计语言和面向过程程序设计

    面向对象程序设计语言和面向过程程序设计语言的区别由哪些 xff1f 面向对象的特点是 xff1a 封装 xff0c 继承 xff0c 多态 xff0c 其中多态又分为重载和重写 xff0c 面向对象的编程思想更接近现实的事物 面向对象的优点
  • Anaconda安装在D盘后无法在CMD窗口执行python命令

    Anaconda安装在D盘后无法在CMD窗口执行python命令以及库的安装办法 解决办法 xff1a 1 添加环境变量 我的电脑 右键属性 高级系统设置 环境变量 双击变量PATH 将Anaconda和Scripts的路径加入即可 xff
  • Ubuntu18.04配置多用户远程桌面控制display 0问题解决

    Ubuntu18 04配置xrdp多用户远程桌面控制 display 0问题解决 找了网上的所有方法都没有解决 xff0c 重新安装xrdp好了一会儿还会出现display 0情况 xff0c 偶然一次配置了ssh xff0c 然后就好了
  • Windows后台运行并启动Frpc客户端界面

    Windows后台运行并启动Frpc客户端界面 frp搭建内网穿透可以看我另外一篇 1 frps服务端配置 common bind port 61 3000 vhost http port 61 4000 vhost https port
  • 树莓派4安装Debian10

    原因 虽然树莓派自家的Raspberry Pi OS也是Debian10 xff0c 但是作为有些强迫症的程序员 xff0c 我还是选择安装官方发布的Debian10 其实安装很简单 xff0c 直接写入内存卡就行了 xff0c 但是不能s
  • Framework中的连接管理机制--4G和wifi之间的切换流程(转)

    https blog csdn net u010961631 article details 48629601 上一节 Wifi服务框架介绍 介绍了WIFI的大致框架 xff0c 本文主要介绍连接管理中的几个重要角色 xff0c 包括 xf
  • pandas学习笔记:pandas.Dataframe.rename()函数用法

    pandas学习笔记 xff1a pandas Dataframe rename 函数用法 pandas Dataframe rename 函数主要是用来修改Dataframe数据的行名和列名 主要用到的参数有 columns xff1a
  • mac os 查看日历_从命令行查看Mac日历

    mac os 查看日历 As someone that loves using UI tools I do pride myself in learning how to accomplish the same feats from com
  • Sql基础教程

    sql基础教程 第一章 数据库与sql1 1 数据库是什么DBMS的种类 1 2 数据库的结构表的结构 1 3 SQL 概要SQL语句及其种类SQL的基本书写规则 1 4 表的创建数据库的创建表的创建命名规则数据类型约束的设置 1 5 表的
  • Jetson TX2更换软件源(转)

    感谢博客 xff1a 点击打开链接 TX2的软件源为国外服务器 xff0c 网速会很慢 xff0c 需要换国内的ARM源 1 备份 etc lib路径下的source list文件 xff0c 然后在终端 xff08 按ctrl 43 al
  • c语言中求数组长度(*的作用,定义指针变量与取值)

    最近在学习c语言 xff0c 在c语言中少了很多库函数 xff0c 就比如我在求数组长度的时候 xff0c len 不能用了 xff0c 这在python中是自带的函数 即使在c 43 43 中 xff0c 求字符串长度时也能用str le
  • 个人使用ubuntu18相关配置

    root登陆 1 首先获得临时的root权限 xff0c 因为后面的一些操作需要root权限才可以 xff0c 打开终端输入以下命令 sudo s 之后直接输入当前账户的密码 xff0c 就可以获得临时的root权限 2 先创建root账户
  • Manjaro内存不足解决记录

    1 Linux虚拟机 最近在家闲得没事做 xff0c 就使用VMware装了一个linux虚拟机 xff0c 虽然之前装过许多linux发行版的虚拟机 xff0c 也装过win10 43 Deepin双系统 xff0c 但是从来没具体了解过
  • String/StringBuilder/StringBuffer

    String StringBuilder StringBuffer 1 可变性 String 字符串常量 xff0c 字符串是不可变的 源码中 xff1a span class token keyword private span span
  • 计算机网络-网络结构

    计算机网络 三种网络结构 OSI xff1a 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层 TCP IP xff1a 网络接口层 网际层 传输层 应用层 五层 xff1a 物理层 数据链路层 网络层 传输层 应用层 应用层 它
  • 计算机网络-三次握手/四次挥手/可靠传输/ARQ协议

    计算机网络 TCP三次握手 同步SYN 确认ACK 发送端 SYN标志的数据报 seq 61 x gt 接收端 xff08 SYN 61 1 xff09 发送端 lt SYN ACK标志的数据报 seq 61 y ack 61 x 43 1
  • 计算机网络-拥塞控制/HTTP/URL

    滑动窗口和流量控制 TCP利用滑动窗口实现流量控制 xff0c 流量控制就是为了控制发送方发送速率 xff0c 保证接收方来得及接收 接收方发送的确认报文中的窗口字段可以来控制发送方窗口大小 xff0c 从而影响发送方的发送速率 xff0c
  • Integer和int进行==比较

    Integer amp int int是Java的基本数据类型 xff0c 而Integer是它的包装类 xff0c 61 61 在进行比较时 xff0c 如果是基本类型 xff0c 比较的是值 xff0c 如果是引用类型 xff0c 比较
  • deepin恢复出厂设置_如何恢复出厂设置

    deepin恢复出厂设置 There comes a time in every user s life when they have to reset something back to its factory default Perha
  • JUC-JMM/Volatile/单例模式

    JMM Java内存模型 xff0c 是一个概念 xff0c 不存在的东西 xff0c 概念 约定 关于JMM的一些同步约定 xff1a 线程解锁前 xff0c 必须把共享变量刷回主存线程加锁前 xff0c 必须读取主存中的最新值到工作内存