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 ...
随机推荐
- jQuery基础入门(一)
jQuery是什么? jQuery是一个JavaScript常用的工具函数库.jQuery是一个轻量级的"写的少,做的多"的JavaScript库. jQuery当中包含有以下一些 ...
- React生命周期和响应式原理(Fiber架构)
注意:只有类组件才有生命周期钩子函数,函数组件没有生命周期钩子函数. 生命周期 装载阶段:constructor() render() componentDidMount() 更新阶段:render( ...
- Three---面向对象与面向过程/属性和变量/关于self/一些魔法方法的使用/继承/super方法/多态
python的面向对象 面向对象与面向过程 面向过程 面向过程思想:需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为,需要自己编写代码(自己来做) 面向对象 面向对象的三 ...
- LuoguP1799 数列_NOI导刊2010提高 (动态规划)
$ f[j]=max(f[i−1][j],f[i−1][j−1]+(x == j) $ #include <iostream> #include <cstdio> #inclu ...
- Vue3 Transition 过渡效果之基于 CSS 过渡
介绍 Transistion 路由组件的切换.动态组件的切换.v-if 条件渲染组件以及 v-show 显示组件原本是没有任何过渡(CSS 动画)效果的.然而,Vue 的内置组件<Transit ...
- C#.NET ORM FreeSql 读取使用 US7ASCII 的 Oracle 数据库中文显示乱码问题
前言 关于 Oracle US7ASCII 中文乱码的问题,Ado.Net 和 Odbc 无法解决.包括最新的.Net Core..NET6..NET7 都无法解决这个问题. FreeSql 对 Or ...
- 【java】学习路径45-多线程-线程生命周期
线程分为五大状态:新建.就绪.运行.阻塞.死亡. New,Runnable,Running,Blocked,Terminated. 新建状态(New: 创建好一个系统对象,在调用start()之前,线 ...
- 使用.Net对图片进行裁剪、缩放、与加水印
图片的裁剪.缩放.与加水印,是任何系统经常要用到的功能,它们现已集成到IUtility工具中,使用十分简便.(具体代码将在文末给出,支持.NET/.NET Framework/.NET Core) 现 ...
- Flume 组件安装配置
下载和解压 Flume 实验环境可能需要回至第四,五,六章(hadoop和hive),否则后面传输数据可能报错(猜测)! 可 以 从 官 网 下 载 Flume 组 件 安 装 包 , 下 载 地 址 ...
- KingbaseES 格式化函数
KingbaseES格式化函数提供一套有效的工具用于把各种数据类型(日期/时间.integer.floating point和numeric)转换成格式化的字符串以及反过来从格式化的字符串转换成指定的 ...