并发编程之三大特性及JMM内存模型

这篇具有很好参考价值的文章主要介绍了并发编程之三大特性及JMM内存模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

原子性

如何保证原子性

可见性

如何保证可见性

有序性

如何保证有序性

Java内存模型(JMM内存模型)

Java内存模型的一些关键概念:

主内存与工作内存交互协议

Java内存模型通过以下手段来确保多线程程序的正确性:

锁机制

volatile

volatile禁止指令重排序

 Happens-Before


并发三大特性

原子性、可见性、有序性

原子性

        原子性是指一个操作是不可中断的。一个原子操作是一个不可分割的整体,要么全部执行成功,要么全部不执行。在多线程环境下,当多个线程访问共享变量时,如果其中一个线程在执行某个操作,其他线程不能同时执行该操作。

public class AtomicTest {
    private static volatile int counter = 0;

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    //synchronized (AtomicTest.class) {
                        counter++;
                   // }
                }
            });
            thread.start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter);
    }
}

        正常执行后counter变量的值远远大于预期10000,加synchronized后counter变量为10000

如何保证原子性

      1. 通过synchronized关键字保证原子性

      2. 通过 Lock锁保证原子性

      3. 通过 CAS保证原子性


可见性

       可见性是指当一个线程修改了共享变量的值时,其他线程能够立即看到这个修改。在多线程环境下,由于线程之间的缓存机制,一个线程对共享变量的修改可能不会立即被其他线程看到。

 

public class VisibilityExample {
    private static boolean stop = false;

    public static void main(String[] args) {
        // 线程1:修改共享变量
        Thread thread1 = new Thread(() -> {
            while (!stop) {
                // do something
            }
            System.out.println("Thread 1 finished");
        });
        // 线程2:修改共享变量
        Thread thread2 = new Thread(() -> {
            stop = true;
            System.out.println("Thread 2 set stop to true");
        });

        // 启动线程1
        thread1.start();

        // 稍等片刻
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 启动线程2
        thread2.start();
    }
}

       在以上代码中,两个线程分别运行,并且共享一个stop变量。线程1在一个循环中检查stop变量是否为false,而线程2在启动后将stop变量设置为true。然而,由于没有同步机制,线程1可能不会立即看到线程2对stop变量的修改,导致线程1一直在循环中无法退出。

private static volatile boolean stop = false;

通过给stop变量增加volatile修饰,即可保证可见性。

如何保证可见性

       通过volatile 关键字保证可见性
       通过内存屏障保证可见性
       通过synchronized 关键字保证可见性
       通过Lock锁保证可见性


有序性

       有序性是指程序执行的顺序按照代码的先后顺序执行。在多线程环境下,由于指令重排序等优化,有时候线程执行的顺序可能与代码的顺序不一致。

public class ReOrderTest {

    private static  int x = 0, y = 0;
    private  static  int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        int i=0;
        while (true) {
            i++;
            x = 0;
            y = 0;
            a = 0;
            b = 0;

            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    //用于调整两个线程的执行顺序
                    shortWait(20000);
                    a = 1; 
                    x = b; 

                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    b = 1;
                    y = a;

                }
            });

            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();

            System.out.println("第" + i + "次(" + x + "," + y + ")");
            if (x==0&&y==0){
                break;
            }
        }
    }
    public static void shortWait(long interval){
        long start = System.nanoTime();
        long end;
        do{
            end = System.nanoTime();
        }while(start + interval >= end);
    }
}

执行结果:x,y出现了0,0的结果,程序终止。出现这种结果有可能是重排序导致的。

如何保证有序性

       通过volatile 关键字保证有序性
       通过内存屏障保证有序性 
       通过synchronized关键字保证有序性
       通过Lock锁保证有序性


Java内存模型(JMM内存模型)

       Java内存模型(Java Memory Model,JMM)是Java程序中多线程并发访问共享变量时,对内存操作行为的一种规范。它定义了在多线程环境中,线程如何与主内存和工作内存交互,以确保并发程序的正确性。

              并发编程之三大特性及JMM内存模型,并发编程,java,开发语言,后端

Java内存模型的一些关键概念:

1. 主内存(Main Memory): 主内存是所有线程共享的内存区域,包含所有的共享变量。所有的线程都可以访问主内存。
2. 工作内存(Working Memory): 每个线程有自己的工作内存,存储了该线程使用到的变量的副本。线程对变量的所有操作都在工作内存中进行,不直接读写主内存。
3. 共享变量: 多个线程之间可以共享的变量,例如类的静态变量或实例变量。
4. 原子性(Atomicity): 单个的读/写操作是原子的,即要么完成,要么不开始。
5. 可见性(Visibility): 当一个线程修改了共享变量的值,其他线程应该能够立即看到这个变化。
6. 有序性(Ordering): 程序执行的顺序与代码中的顺序一致。

主内存与工作内存交互协议

从工 作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种原子操作来完成:
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁 定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要 使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机 遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操 作。
write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

并发编程之三大特性及JMM内存模型,并发编程,java,开发语言,后端

Java内存模型通过以下手段来确保多线程程序的正确性:

1. 锁机制: 使用synchronized关键字或显式锁来保护共享数据,确保在同一时刻只有一个线程可以访问临界区。
2. volatile关键字: 用于修饰共享变量,保证可见性。对一个volatile变量的写操作将立即刷新到主内存,而读操作将从主内存中加载最新的值。
3. final关键字: 被final修饰的字段在构造函数结束之前就已经对其他线程可见。
4. Happens-Before关系: JMM定义了Happens-Before的规则,确保在某个操作之前的操作对其可见。

锁机制

1. 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。
2. 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。

       synchronized关键字的作用是确保多个线程访问共享资源时的互斥性和可见性。在获取锁之前,线程 会将共享变量的最新值从主内存中读取到线程本地的缓存中,释放锁时会将修改后的共享变量的值刷 新到主内存中,以保证可见性。

volatile

1. 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
2. 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。

volatile禁止指令重排序

并发编程之三大特性及JMM内存模型,并发编程,java,开发语言,后端

 Happens-Before

       Happens-Before是Java内存模型(Java Memory Model,JMM)中一个重要的概念,用于规定多线程程序中操作之间的执行顺序和可见性关系。这个概念确保了在程序中对共享变量的操作是按照一定顺序执行的,以避免出现不确定的结果和数据竞争问题。

       如果操作A在操作B之前发生在之前(happens-before),那么操作A对于操作B来说,必定是可见的,而且操作A的效果将被操作B看到。  

Happens-Before部分关系规则:

1. 程序顺序规则(Program Order Rule): 在一个线程中,操作按照程序代码的先后顺序执行。
2. 锁定规则(Lock Rule): 释放锁的操作Happens-Before于后续对同一锁的加锁操作。
3. volatile变量规则(Volatile Variable Rule): 对volatile变量的写操作Happens-Before于后续对这个变量的读操作。
4. 传递性(Transitivity): 如果操作A Happens-Before于操作B,且操作B Happens-Before于操作C,那么操作A Happens-Before于操作C。
5. 线程启动规则(Thread Start Rule): 线程的启动操作Happens-Before于该线程的任何操作。
6. 线程终止规则(Thread Termination Rule): 线程的所有操作Happens-Before于其他线程检测到该线程已经终止。文章来源地址https://www.toymoban.com/news/detail-805601.html

到了这里,关于并发编程之三大特性及JMM内存模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • [JVM] 浅谈JMM(Java 内存模型)

    Java 内存模型(Java Memory Model,JMM)是 Java 虚拟机规范中定义的一种抽象计算机内存模型,用于描述 Java 程序在多线程下的内存访问行为。JMM 定义了线程之间共享变量的可见性和有序性规则,为开发者提供了一种可靠的同步机制,以避免并发程序中常见的线程安全问题。 JMM

    2024年01月16日
    浏览(15)
  • Java内存区域(运行时数据区域)和内存模型(JMM)

    Java内存区域(运行时数据区域)和内存模型(JMM)

    Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。 而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发

    2024年02月12日
    浏览(16)
  • Java 内存模型(JMM)探寻原理,深度讲解

    Java 内存模型(JMM)探寻原理,深度讲解

    目录 一. 前言 二. 为什么会有内存模型 2.1. 硬件内存架构 2.2. 缓存一致性问题 2.3. 处理器优化和指令重排序 三. 并发编程的问题 四. Java 内存模型(JMM) 4.1. Java 运行时内存区域与硬件内存的关系 4.2. Java 线程与主内存的关系 4.3. 线程间通信 五. 主内存和工作内存 六. J

    2024年04月22日
    浏览(11)
  • Java内存模型(JMM)和volatile原理

    Java内存模型(JMM)和volatile原理

    目录 一、Java 内存模型 二、可见性 三、有序性 四、volatile原理  1、可见性保证 2、有序性保证 五、线程安全的单例 JMM即Java Memory Model,他定义了 主存(共享的数据) 、 工作内存(私有的数据) 抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等 JMM体现以下

    2024年02月09日
    浏览(8)
  • 【Java多线程学习7】JMM(Java内存模型)学习

    【Java多线程学习7】JMM(Java内存模型)学习

    JMM(Java内存模型),可以看作是 Java定义的并发编程相关的一组规范 ,除了抽象了 线程和主内存 之间的关系之外,其还规定了从 Java源代码 到 CPU可执行指令 的这个转化过程中要遵守哪些并发相关的原则和规范,其主要目的是 简化多线程编程 , 增强程序的可移植性 。 至于

    2024年02月11日
    浏览(14)
  • java八股文面试[多线程]——并发三大特性 原子 可见 顺序

    java八股文面试[多线程]——并发三大特性 原子 可见 顺序

        AutomicInteger :  volatile + CAS 总线LOCK  MESI 两个协议 TODO volatile的可见性和禁止重排序是怎么实现的: DCL场景:  new操作会在字节码层面生成两个步骤: 分配内存、调用构造器 然后把引用赋值给singleton 不加volatile则会发生指令重排,可能得到不完整的对象 知识来源: 【并

    2024年02月11日
    浏览(16)
  • 区分什么是Java内存模型(JMM)和 JVM运行时数据区

    区分什么是Java内存模型(JMM)和 JVM运行时数据区

    Java的内存区域和内存模型是不一样的东西,内存区域是指 JVM 运行时将数据分区域存储,强调对内存空间的划分 。 而内存模型(Java Memory Model,简称 JMM )是 定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式 ,如果我们要想深入了解Java并

    2024年02月11日
    浏览(12)
  • 并发编程_jmm部分

    并发编程_jmm部分

    前提:并发编程有3大问题,可见性、有序性、原子性。 导致 可见性 的原因是缓存, 有序性 的原因是 编译器优化。解决方法就是直接禁用缓存和编译器优化,导致程序性能堪忧。 因此合理的方案就是 按需 禁用缓存和编译器优化。 JMM定义了线程和主内存之间的抽象关系:

    2024年02月13日
    浏览(6)
  • 【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】

    【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】

    volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值 将改变后的副本的值从工作内存 刷新到主内存 代码在读取 volatile 修饰的变量的时候 从主内存中读取volatile变量的最新值到

    2024年02月13日
    浏览(13)
  • 【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】【保证内存可见 和 指令有序】

    【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】【保证内存可见 和 指令有序】

    volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值 将改变后的副本的值从工作内存 刷新到主内存 代码在读取 volatile 修饰的变量的时候 从主内存中读取volatile变量的最新值到

    2024年02月16日
    浏览(13)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包