zoukankan      html  css  js  c++  java
  • 13、JMM(线程同步机制的约定)

    引用学习(狂神说)

    什么是JMM?

    JMM:Java内存模型,不存在的东西,是一种概念!是一种约定!隶属于JVM。

    关于JMM的一些同步的约定:

    主存和线程之间的关系图:

    正因为它们之间存在这种关系(线程有自己的工作内存),所以才会有JMM的约定,约定如下:

    1、线程在解锁前,必须把共享的变量立刻刷新回主存!

    2、线程在加锁前,必须读取主存中最新的值到工作内存(线程有自己的工作内存)中!

    3、加锁和解锁是同一把锁

    线程的8种操作和对应工作方式

    概念图

     框框中对应的是一组操作,总共有8个操作

    工作方式

    1. 线程在加锁(lock)之前读取主存的最新的变量

    2. 线程A需要访问主存的变量,那么会读取(read)到一个空间,再从这个空间加载(load)线程A自己的工作内存。

    3. 加载到工作内存的变量,会给执行引擎使用(use),使用完成,将数据赋值(assign)回工作内存

    4. 线程在解锁之前将数据写出(write)到一个空间,再存储(store)回主存中。

    8种操作

    内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

    • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态

    • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

    • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便

    • 随后的load动作使用

      • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

    • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

    • 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操作之前,必须把此变量同步回主内存

    问题:程序不知道的内存的值已经被修改过了,怎么办?

    下面是模拟这种情况的图:

    • 线程A一开始读取到的值是flag = true,但是线程A一直在操作变量,没有执行完毕

    • 线程B在这个时候,修改了变量为flag=false,并将值刷新会主存

    • 那么线程A就不知道主存已经做出了修改。

     用程序模拟出上面出现的情况

    package com.zxh.testValidate;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo01 {
    
        private static int num = 0; // 内存的变量
    
        public static void main(String[] args) {
            // 线程A不断对num值进行访问
            new Thread(()->{
                while(num == 0){
    
                }
            }).start();
    
            // 为了保证线程A先启动,进行延迟1s,否则会导致num直接=1
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            // main线程在另一线程还在运行时,将num变为1
            num = 1;
            System.out.println(num);    // 输出是否被修改
        }
    }

    线程A一直循环,程序一直在运行,不知道值已经被修改,那该怎么办呢?

     

    那就涉及到volatile这个关键字了

     

    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    闲来无事,编写一个数据迁移小工具
    Moq基础
    探索逻辑事务 TransactionScope
    IntelliJ IDEA安装及jsp开发环境搭建
    数据结构整理(二) 树
    数据结构整理(一) 线性结构
    梳理delegate相关概念
    02_Android应用界面编程_01_视图(View)组件
    01_Android应用开发环境_05_签名android应用程序
    01_Android应用开发环境_04_Android常用开发工具的用法
  • 原文地址:https://www.cnblogs.com/zxhbk/p/13027985.html
Copyright © 2011-2022 走看看