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的更多相关文章

  1. java架构之路(多线程)JMM和volatile关键字

    说到JMM大家一定很陌生,被我们所熟知的一定是jvm虚拟机,而我们今天讲的JMM和JVM虚拟机没有半毛钱关系,千万不要把JMM的任何事情联想到JVM,把JMM当做一个完全新的事物去理解和认识. 我们先 ...

  2. Java多线程(7):JUC(上)

    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 前面把线程相关的生命周期.关键字.线程池(ThreadPool).ThreadLocal.CAS.锁和AQS都讲完了,现在就剩下怎么来用多线程了 ...

  3. java架构之路(多线程)JMM和volatile关键字(二)

    貌似两个多月没写博客,不知道年前这段时间都去忙了什么. 好久以前写过一次和volatile相关的博客,感觉没写的那么深入吧,这次我们继续说我们的volatile关键字. 复习: 先来简单的复习一遍以前 ...

  4. JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题

    1.线程和进程 进程:一个程序,微信.qq...程序的集合.(一个进程包含多个线程,至少包含一个线程.java默认有两个线程:主线程(main).垃圾回收线程(GC) 线程:runnable.thre ...

  5. JVM学习(七)JMM内存模型

    一.什么是JMM 概念:Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能 ...

  6. Java并发编程的艺术(四)——JMM、重排序、happens-before

    什么是JMM JMM就是Java内存模型.目的是为了屏蔽系统和硬件的差异,让同一代码在不同平台下能够达到相同的访问结果.规定了线程和内存之间的关系. 内存划分 JMM规定了内存主要划分为主内存和工作内 ...

  7. JUC(10)深入理解CAS和ABA

    文章目录 1.CAS 2.原子引用解决ABA问题,版本号.修改后,可以看到 1.CAS package com.cas; import java.util.concurrent.atomic.Atom ...

  8. JUC(7)四大函数式接口

    文章目录 1.四大函数式接口(必须掌握) 1.1 function 1.2 Predicate 1.3 Consumer 1.4 Supplier 1.四大函数式接口(必须掌握) 1.lambda表达 ...

  9. JUC(5)BlockingQueue四组API

    1.读写锁ReadWriteLock package com.readlock; import java.util.HashMap; import java.util.Map; /** * ReadW ...

随机推荐

  1. Spring 01 概述

    简介 Spring 是开源的轻量级 J2EE 框架 我们常说的 Spring 实际上是指 Spring Framework,它是 Spring 家族中的一个重要分支. 官方文档 https://doc ...

  2. .NET性能优化-快速遍历List集合

    简介 System.Collections.Generic.List<T>是.NET中的泛型集合类,可以存储任何类型的数据,因为它的便利和丰富的API,在我们平时会广泛的使用到它,可以说是 ...

  3. Vue+Koa+MongoDB从零打造一个任务管理系统

    大概是在18年的时候,当时还没有疫情.当时工作中同时负责多个项目,有 PC 端运营管理后台的,有移动端 M 站的,有微信小程序的,每天 git 分支切到头昏眼花,每个需求提测需要发送邮件,而且周五要写 ...

  4. 209. 长度最小的子数组--LeetCode

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/minimum-size-subarray-sum 著作权归领扣网络所有.商业转载请联系官方授权,非商业 ...

  5. React报错之Parameter 'props' implicitly has an 'any' type

    正文从这开始~ 总览 当我们没有为函数组件或者类组件的props声明类型,或忘记为React安装类型声明文件时,会产生"Parameter 'props' implicitly has an ...

  6. day01-GUI坦克大战01

    JavaGUI-坦克大战 1.Java绘图坐标体系 坐标体系介绍:下图说明了一个Java坐标体系.坐标原点位于左上角,以像素为单位.在Java坐标体系中,第一个是x坐标,表示当前位置为水平方向,距离坐 ...

  7. KingbaseES V8R3 集群专用机网关失败分析案例

    ​ KingbaseES R3集群网关检测工作机制: 1.Cluster下watchdog进程在固定间隔时间,通过ping 网关地址监控链路的连通性,如果连通网关地址失败,则修改cluster sta ...

  8. python 数据挖掘模块学习

    项目中用到的模块 API # 模块: import pandas as pd import numpy as np from scipy.optimize import curve_fit numpy ...

  9. 树莓派学习笔记 (1) - 安装&初始设置

    1. 设备 Raspberry Pi 4B MicorSD card (tf 卡) Windows 10 电脑 Android 手机 2. 烧录系统 利用官网提供的 Raspberry Pi Imag ...

  10. 阿里云服务器如何使用phpmailer发送邮件

    原因是因为阿里云把25端口给禁用了,所以,普通方式发送不了,解决办法就是在阿里云安全组中把465端口打开,然后再才能发送邮件.亲测成功,贴出引用代码 function emailTo($user,$c ...