学习Java内存模型JMM心得

学习Java内存模型JMM心得

什么是Java内存模型

Java内存模型(Java Memory Model,JMM)是一种用于保证在多线程情况下共享变量的可见性和有序性的机制。

JMM的核心概念

Java内存模型中有三个核心概念:原子性、可见性和有序性。

原子性

原子性指的是在同一时间只有一个线程可以访问共享变量。Java中的基本数据类型,如int、long等,都是具有原子性的。但是,对于复合操作,如i++,实际上是由多个步骤组成的,因此需要通过synchronized或者java.util.concurrent.locks中的锁机制来保证原子性。

可见性

可见性指的是一个线程对共享变量的修改,能够被其他线程及时地看到。在Java中,可以使用volatile关键字来保证可见性。通过volatile关键字,对该变量进行读操作时会直接从内存中读取最新的值,对该变量进行写操作时会立即将修改的值刷回到内存中。

有序性

有序性指的是指令执行的顺序,Java中通过happens-before的规则来保证有序性。happens-before的规则规定了一些时间上的先后顺序,例如在一个线程中,先发生的指令会先执行;在一个锁里,先释放锁的线程的操作happens-before于后获取锁的线程中的操作。

JMM的示例说明

示例1:通过volatile关键字保证变量可见性

下面是一段代码,其中共享变量flag未使用volatile关键字修饰:

class MyThread implements Runnable {
    private boolean flag = false;

    public void run() {
        while (!flag) {
            //Do something
        }
        System.out.println("Thread stopped.");
    }

    public void stop() {
        this.flag = true;
    }
}

public class VolatileTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t = new Thread(myThread);
        t.start();
        Thread.sleep(1000);
        myThread.stop();
    }
}

在上面代码中,flag被一个线程修改后,另一个线程无法及时地感知到其变化。因此,程序无法正常停止。现在我们通过将flag变量使用volatile关键字修饰,使其变为可见的。

class MyThread implements Runnable {
    private volatile boolean flag = false;

    public void run() {
        while (!flag) {
            //Do something
        }
        System.out.println("Thread stopped.");
    }

    public void stop() {
        this.flag = true;
    }
}

public class VolatileTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t = new Thread(myThread);
        t.start();
        Thread.sleep(1000);
        myThread.stop();
    }
}

示例2:使用锁机制保证原子性

下面是一个简单的例子,使用volatile关键字不足以保证原子性。

class MyRunnable implements Runnable {
    private volatile int count = 0;

    public void run() {
        for (int i = 0; i < 100000; i++) {
            count++;
        }
        System.out.println("count: " + count);
    }
}

public class VolatileAtomicityTest {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

在上面的代码中,我们使用volatile关键字保证了count变量的可见性,但是由于count++操作是一个复合操作,因此并不具备原子性。运行上面的代码,就会发现输出的count的值并不是预期的200000。现在我们可以使用锁机制来保证原子性。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class MyRunnable implements Runnable {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void run() {
        for (int i = 0; i < 100000; i++) {
            lock.lock();
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }
        System.out.println("count: " + count);
    }
}

public class LockAtomicityTest {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

在上述代码中,我们使用了Lock机制来保证了count变量的原子性。运行上面的代码,输出的count的值为200000,证明了操作的原子性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:学习Java内存模型JMM心得 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Mac OS下为Android Studio编译FFmpeg解码库的详细教程

    下面是Mac OS下为Android Studio编译FFmpeg解码库的详细教程: 1. 安装 homebrew homebrew 是 Mac OS 上的包管理器,可以通过命令行轻松安装各种软件和工具。 安装 homebrew,可以在终端内运行以下命令: /bin/bash -c "$(curl -fsSL https://raw.githubu…

    Java 2023年5月20日
    00
  • 常见的Java代码混淆工具有哪些?

    常见的Java代码混淆工具有以下几种: ProGuard: ProGuard 是一款开源的Java代码混淆工具,通过删除未使用的类、字段、方法和属性,重命名它们,使得反编译后的代码难以阅读和理解。使用方法如下: 1.1. 首先下载并安装 ProGuard 工具,可以从官方网站 https://sourceforge.net/projects/proguard…

    Java 2023年5月11日
    00
  • Java中byte[]、String、Hex字符串等转换的方法

    下面就是Java中byte[]、String、Hex字符串等转换的方法的详细攻略。 byte[]和String之间的转换 在Java中,byte[]和String之间的互相转换是非常常见的操作,常用的方法有: byte[] -> String 通过String类的构造函数来将byte[]转换为String: byte[] bytes = {97, 98…

    Java 2023年5月26日
    00
  • Spring Boot 利用 XML 方式整合 MyBatis

    Spring Boot 利用 XML 方式整合 MyBatis攻略 本文将介绍使用 Spring Boot 通过 XML 配置方式整合 MyBatis 的完整流程,并提供两个示例。 1. 添加依赖 在 pom.xml 文件中添加如下依赖: <dependency> <groupId>org.mybatis</groupId&gt…

    Java 2023年5月20日
    00
  • 面向对象编程依赖注入详解

    面向对象编程依赖注入详解 什么是依赖注入 依赖注入(Dependency Injection,简称DI)是一种在面向对象编程中,将类间依赖关系的创建和管理权交给其他专门的类来处理的技术。通俗的说,就是让调用类摆脱创建和管理被调用类对象的束缚,将创建和管理依赖对象的工作交给容器来完成。 DI的优点 降低了系统模块间的耦合度。 可以提高模块的可重用性、可测试性和…

    Java 2023年5月26日
    00
  • 关于如何正确地定义Java内部类方法详解

    下面是关于如何正确地定义Java内部类方法的详细讲解: 定义内部类方法的方法 要定义Java内部类方法,你需要按照以下步骤进行操作: 1.在外部类中定义内部类 public class OuterClass { private int outerField; public void outerMethod() { InnerClass innerObject…

    Java 2023年5月19日
    00
  • 一文详解JAVA中InputStreamReader流

    一、概述 InputStreamReader是Java中的输入流,是字符流与字节流之间的桥梁。它将字节流转换为字符流,以便于阅读和操作。 二、用法 InputStreamReader的用法非常简单,只需要创建一个InputStreamReader实例,并且为其传入一个输入流,然后就可以操作输入流中的字符了。 示例代码如下: try { InputStream…

    Java 2023年5月20日
    00
  • Java简易抽奖系统小项目

    Java简易抽奖系统小项目攻略 系统需求 本系统需要Java环境和命令行界面,可以在Windows、Linux和macOS等平台上运行。 实现步骤 第一步:初始化 本系统需要一个抽奖池,因此我们可以创建一个ArrayList来保存所有的奖品信息。同时,我们需要引入java.util.Random类生成随机数。 import java.util.ArrayLi…

    Java 2023年5月30日
    00
合作推广
合作推广
分享本页
返回顶部