JUC(8)JMM
文章目录
1、JMM
Volatile是java虚拟机提供轻量级的同步机制
- 1、保证可见性
- 2、不保证原子性
- 3、禁止指令重排
什么是JMM
java内存模型,不存在的东西,概念、约定
- 1、线程解锁前,必须把共享变量立刻刷回主存(线程将主存中的变量复制一份到线程中)
- 2、线程加锁前,必须读取主存中的最新值到工作内存中
- 3、加锁和读锁是同一把锁

两个线程同时执行,假如一个线程修改变量、另外一个线程没有及时得到,就会出错。

内存交互操作
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- u- se (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
不允许一个线程将没有assign的数据从工作内存同步回主内存 - 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
举例子
package com.jmm;
import java.util.concurrent.TimeUnit;
public class JMMDemo {
private static int num = 0;
public static void main(String[] args) {
new Thread(()->{//线程1对主内存的变化不知道的
while (num == 0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
num =1;
System.out.println(num);
}
}
测试结果

2、volatile
保证可见性
private volatile static int num = 0;
测试结果:程序立刻停止

不保证原子性
package com.jmm;
/**
* 不保证原子性
*/
public class VolatileDemo {
private volatile static int num = 0;
public static void add(){
num ++;
}
public static void main(String[] args) {
for (int i = 0; i < 200; 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,怎样保证原子性
使用原子类



测试结果

指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的
volatile可以避免指令重排
volatile是可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象
3、单例模式
饿汉式
package com.single;
/**
* 饿汉式单例
*/
public class HungrySingle {
private HungrySingle(){}
private final static HungrySingle HUNGRY_SINGLE = new HungrySingle();
public static HungrySingle getInstance(){
return HUNGRY_SINGLE;
}
}
懒汉式
package com.single;
/**
* 懒汉式单例
*/
public class LazySingle {
private LazySingle(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazySingle lazySingle;
/**
*双重检测锁模式的 懒汉式单例 DCL懒汉式
*/
public static LazySingle getInstance(){
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
lazySingle = new LazySingle();
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
}
}
}
return lazySingle;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
getInstance();
}).start();
}
}
}
JUC(8)JMM的更多相关文章
- java架构之路(多线程)JMM和volatile关键字
说到JMM大家一定很陌生,被我们所熟知的一定是jvm虚拟机,而我们今天讲的JMM和JVM虚拟机没有半毛钱关系,千万不要把JMM的任何事情联想到JVM,把JMM当做一个完全新的事物去理解和认识. 我们先 ...
- Java多线程(7):JUC(上)
您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 前面把线程相关的生命周期.关键字.线程池(ThreadPool).ThreadLocal.CAS.锁和AQS都讲完了,现在就剩下怎么来用多线程了 ...
- java架构之路(多线程)JMM和volatile关键字(二)
貌似两个多月没写博客,不知道年前这段时间都去忙了什么. 好久以前写过一次和volatile相关的博客,感觉没写的那么深入吧,这次我们继续说我们的volatile关键字. 复习: 先来简单的复习一遍以前 ...
- JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
1.线程和进程 进程:一个程序,微信.qq...程序的集合.(一个进程包含多个线程,至少包含一个线程.java默认有两个线程:主线程(main).垃圾回收线程(GC) 线程:runnable.thre ...
- JVM学习(七)JMM内存模型
一.什么是JMM 概念:Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能 ...
- Java并发编程的艺术(四)——JMM、重排序、happens-before
什么是JMM JMM就是Java内存模型.目的是为了屏蔽系统和硬件的差异,让同一代码在不同平台下能够达到相同的访问结果.规定了线程和内存之间的关系. 内存划分 JMM规定了内存主要划分为主内存和工作内 ...
- JUC(10)深入理解CAS和ABA
文章目录 1.CAS 2.原子引用解决ABA问题,版本号.修改后,可以看到 1.CAS package com.cas; import java.util.concurrent.atomic.Atom ...
- JUC(7)四大函数式接口
文章目录 1.四大函数式接口(必须掌握) 1.1 function 1.2 Predicate 1.3 Consumer 1.4 Supplier 1.四大函数式接口(必须掌握) 1.lambda表达 ...
- JUC(5)BlockingQueue四组API
1.读写锁ReadWriteLock package com.readlock; import java.util.HashMap; import java.util.Map; /** * ReadW ...
随机推荐
- 一文带你了解webrtc基本原理(动手实现1v1视频通话)
webrtc (Web Real-Time Communications) 是一个实时通讯技术,也是实时音视频技术的标准和框架. 大白话讲,webrtc是一个集大成的实时音视频技术集,包含了各种客户端 ...
- 学习javascript知识
开始学习了 努力----努力----努力 从今天开始 绝不 三天打鱼两天晒网 先把基础再巩固一下
- 论语音社交视频直播平台与 Apache DolphinScheduler 的适配度有多高
在 Apache DolphinScheduler& Apache ShenYu(Incubating) Meetup 上,YY 直播 软件工程师 袁丙泽 为我们分享了<YY直播基于Ap ...
- Luogu2543[AHOI2004]奇怪的字符串 (动态规划 LCS)
04年的省选这么water吗,开个滚动数组算了 #include <iostream> #include <cstdio> #include <cstring> # ...
- monodepth2学习1-原理介绍
monodepth2介绍 monodepth2是在2019年CVPR会议上提出的一种三维重建算法,monodepth2是基于monodepth进行了改进,采用的是基于自监督的神经网络,提出了一下三点优 ...
- 初次认识 Canvas
画布的概念 Canvas(画布)可以用于动画.游戏画面.数据可视化.图片编辑以及实时视频处理等方面.画布在 HTML5 中是通过canvas标签来表现,通过 JavaScript 提供的画布 API, ...
- LOJ2312 LUOGU-P3733「HAOI2017」八纵八横 (异或线性基、生成树、线段树分治)
八纵八横 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个 ...
- HTML创建访问加密代码
在</head>前面加入即可 普通方式 此方法屏蔽F12查看源码但是屏蔽不了Ctrl+U查看源码 解决方式加密html即可注意!解密比较繁琐切记要记住自己设置的密码 <SCRIPT ...
- vue2和vue3的区别?
vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...
- 大家都能看得懂的源码之ahooks useInfiniteScroll
本文是深入浅出 ahooks 源码系列文章的第十七篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 useInfiniteScroll 封装了常见的无限滚动逻 ...