Table of Contents
1. What is JMM
2. What does JMM define?
Atomicity
Visibility
Orderliness
Three and eight types of memory interaction operations
四、volatile关键字
可见性
volatile一定能保证线程安全吗
禁止指令重排序
volatile禁止指令重排序的原理
Home Java javaTutorial Analysis of JMM High Concurrency Programming Examples in Java

Analysis of JMM High Concurrency Programming Examples in Java

May 02, 2023 pm 06:52 PM
java jmm

1. What is JMM

JMM is the Java memory model. Because there are certain differences in memory access under different hardware manufacturers and different operating systems, various problems will occur when the same code runs on different systems. Therefore, the Java Memory Model (JMM) shields the memory access differences of various hardware and operating systems to achieve consistent concurrency effects for Java programs on various platforms.

The Java memory model stipulates that all variables are stored in main memory, including instance variables and static variables, but does not include local variables and method parameters. Each thread has its own working memory. The thread's working memory stores the variables used by the thread and a copy of the main memory. The thread's operations on variables are all performed in the working memory. Threads cannot directly read or write variables in main memory.

Different threads cannot access variables in each other's working memory. The transfer of variable values ​​between threads needs to be completed through main memory.

Analysis of JMM High Concurrency Programming Examples in Java

The working memory of each thread is independent. Thread operation data can only be performed in the working memory and then flushed back to the main memory. This is the basic way threads work as defined by the Java memory model.

A warm reminder, some people here will misunderstand the Java memory model as the Java memory structure, and then answer the questions about heap, stack, and GC garbage collection. In the end, it is far from the question the interviewer wants to ask. In fact, when asked about the Java memory model, they generally want to ask questions related to multi-threading and Java concurrency.

2. What does JMM define?

This is simple. The entire Java memory model is actually built around three characteristics. They are: atomicity, visibility, and orderliness. These three characteristics can be said to be the foundation of the entire Java concurrency.

Atomicity

Atomicity means that an operation is indivisible and uninterruptible, and a thread will not be interfered with by other threads during execution.

The interviewer took a pen and wrote a piece of code. Can the following lines of code guarantee atomicity?

int i = 2;
int j = i;
i++;
i = i + 1;
Copy after login

The first sentence is a basic type assignment operation, which must be an atomic operation.

The second sentence reads the value of i first, and then assigns it to j. This two-step operation cannot guarantee atomicity.

The third and fourth sentences are actually equivalent. First read the value of i, then 1, and finally assign it to i. It is a three-step operation and cannot guarantee atomicity.

JMM can only guarantee basic atomicity. If you want to ensure the atomicity of a code block, it provides two bytecode instructions, monitorenter and moniterexit, which are the synchronized keyword. Therefore operations between synchronized blocks are atomic.

Visibility

Visibility means that when a thread modifies the value of a shared variable, other threads can immediately know that it has been modified. Java uses the volatile keyword to provide visibility. When a variable is modified volatile, the variable will be flushed to the main memory immediately after being modified. When other threads need to read the variable, they will read the new value from the main memory. Ordinary variables cannot guarantee this.

In addition to the volatile keyword, final and synchronized can also achieve visibility.

The principle of synchronized is that after execution is completed and before entering unlock, the shared variables must be synchronized to the main memory.

Final modified fields, once initialization is completed, will be visible to other threads if no object escapes (meaning that the object can be used by other threads after initialization is completed).

Orderliness

In Java, you can use synchronized or volatile to ensure the orderliness of operations between multiple threads. There are some differences in the implementation principles:

The volatile keyword uses memory barriers to prohibit instruction reordering to ensure orderliness.

The principle of synchronized is that after a thread is locked, it must be unlocked before other threads can re-lock, so that the code blocks wrapped in synchronized are executed serially among multiple threads.

Three and eight types of memory interaction operations

There are 8 types of memory interaction operations:

  • ##lock (lock), which acts on variables in main memory , marking the variable as thread-exclusive.

  • read (read), acts on variables in main memory, and transfers the value of the variable from main memory to the working memory of the thread for use in the next load operation.

  • load (load), acts on the variables of the working memory, and puts the variables of the main memory of the read operation into the variable copy of the working memory.

  • use (use), acts on variables in the working memory, transfers the variables in the working memory to the execution engine, whenever the virtual machine encounters a byte that needs to use the value of the variable This operation will be performed when the code command is issued.

  • assign (assignment), acts on a variable in the working memory. It assigns a value received from the execution engine to a copy of the variable in the working memory. Whenever the virtual machine encounters a This operation will be performed when assigning a bytecode instruction to a variable.

  • store (storage), which acts on variables in working memory. It transfers the value of a variable in working memory to main memory for subsequent write use.

  • write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

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

我再补充一下JMM对8种内存交互操作制定的规则吧:

  • 不允许read、load、store、write操作之一单独出现,也就是read操作后必须load,store操作后必须write。

  • 不允许线程丢弃他最近的assign操作,即工作内存中的变量数据改变了之后,必须告知主存。

  • 不允许线程将没有assign的数据从工作内存同步到主内存。

  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过load和assign操作。

  • 一个变量同一时间只能有一个线程对其进行lock操作。多次lock之后,必须执行相同次数unlock才可以解锁。

  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值。在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值。

  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量。

  • 一个线程对一个变量进行unlock操作之前,必须先把此变量同步回主内存。

四、volatile关键字

很多并发编程都使用了volatile关键字,主要的作用包括两点:

  • 保证线程间变量的可见性。

  • 禁止CPU进行指令重排序。

可见性

volatile修饰的变量,当一个线程改变了该变量的值,其他线程是立即可见的。普通变量则需要重新读取才能获得最新值。

volatile保证可见性的流程大概就是这个一个过程:

Analysis of JMM High Concurrency Programming Examples in Java

volatile一定能保证线程安全吗

先说结论吧,volatile不能一定能保证线程安全。

怎么证明呢,我们看下面一段代码的运行结果就知道了:

public class VolatileTest extends Thread {
private static volatile int count = 0;
public static void main(String[] args) throws Exception {
Vector<Thread> threads = new Vector<>();
for (int i = 0; i < 100; i++) {
VolatileTest thread = new VolatileTest();
threads.add(thread);
thread.start();
}
//等待子线程全部完成
for (Thread thread : threads) {
thread.join();
}
//输出结果,正确结果应该是1000,实际却是984
System.out.println(count);//984
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
//休眠500毫秒
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
}
}
Copy after login

为什么volatile不能保证线程安全?

很简单呀,可见性不能保证操作的原子性,前面说过了count++不是原子性操作,会当做三步,先读取count的值,然后+1,最后赋值回去count变量。需要保证线程安全的话,需要使用synchronized关键字或者lock锁,给count++这段代码上锁:

private static synchronized void add() {
count++;
}
Copy after login

禁止指令重排序

首先要讲一下as-if-serial语义,不管怎么重排序,(单线程)程序的执行结果不能被改变。

为了使指令更加符合CPU的执行特性,最大限度的发挥机器的性能,提高程序的执行效率,只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致,这个过程就叫做指令的重排序。

重排序的种类分为三种,分别是:编译器重排序,指令级并行的重排序,内存系统重排序。整个过程如下所示:

Analysis of JMM High Concurrency Programming Examples in Java

指令重排序在单线程是没有问题的,不会影响执行结果,而且还提高了性能。但是在多线程的环境下就不能保证一定不会影响执行结果了。

所以在多线程环境下,就需要禁止指令重排序。

volatile关键字禁止指令重排序有两层意思:

  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行。

  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

下面举个例子:

private static int a;//非volatile修饰变量
private static int b;//非volatile修饰变量
private static volatile int k;//volatile修饰变量
private void hello() {
a = 1; //语句1
b = 2; //语句2
k = 3; //语句3
a = 4; //语句4
b = 5; //语句5
//...
}
Copy after login

变量a,b是非volatile修饰的变量,k则使用volatile修饰。所以语句3不能放在语句1、2前,也不能放在语句4、5后。但是语句1、2的顺序是不能保证的,同理,语句4、5也不能保证顺序。

并且,执行到语句3的时候,语句1,2是肯定执行完毕的,而且语句1,2的执行结果对于语句3,4,5是可见的。

volatile禁止指令重排序的原理

首先要讲一下内存屏障,内存屏障可以分为以下几类:

  • LoadLoad barrier: For statements like Load1, LoadLoad, Load2. Before the data to be read in Load2 and subsequent read operations is accessed, it is guaranteed that the data to be read in Load1 has been read.

  • StoreStore barrier: For such statements Store1, StoreStore, Store2, before Store2 and subsequent write operations are executed, ensure that the write operation of Store1 is visible to other processors.

  • LoadStore barrier: For statements such as Load1, LoadStore, and Store2, ensure that the data to be read by Load1 is completely read before Store2 and subsequent write operations are flushed out.

  • StoreLoad barrier: For such statements Store1, StoreLoad, and Load2, before Load2 and all subsequent read operations are executed, it is guaranteed that the writing of Store1 is visible to all processors.

Insert a LoadLoad barrier after each volatile read operation and a LoadStore barrier after a read operation.

Analysis of JMM High Concurrency Programming Examples in Java

Insert a StoreStore barrier in front of each volatile write operation and a SotreLoad barrier in the back.

Analysis of JMM High Concurrency Programming Examples in Java

The above is the detailed content of Analysis of JMM High Concurrency Programming Examples in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1664
14
PHP Tutorial
1266
29
C# Tutorial
1239
24
Break or return from Java 8 stream forEach? Break or return from Java 8 stream forEach? Feb 07, 2025 pm 12:09 PM

Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

PHP: A Key Language for Web Development PHP: A Key Language for Web Development Apr 13, 2025 am 12:08 AM

PHP is a scripting language widely used on the server side, especially suitable for web development. 1.PHP can embed HTML, process HTTP requests and responses, and supports a variety of databases. 2.PHP is used to generate dynamic web content, process form data, access databases, etc., with strong community support and open source resources. 3. PHP is an interpreted language, and the execution process includes lexical analysis, grammatical analysis, compilation and execution. 4.PHP can be combined with MySQL for advanced applications such as user registration systems. 5. When debugging PHP, you can use functions such as error_reporting() and var_dump(). 6. Optimize PHP code to use caching mechanisms, optimize database queries and use built-in functions. 7

PHP vs. Python: Understanding the Differences PHP vs. Python: Understanding the Differences Apr 11, 2025 am 12:15 AM

PHP and Python each have their own advantages, and the choice should be based on project requirements. 1.PHP is suitable for web development, with simple syntax and high execution efficiency. 2. Python is suitable for data science and machine learning, with concise syntax and rich libraries.

PHP vs. Other Languages: A Comparison PHP vs. Other Languages: A Comparison Apr 13, 2025 am 12:19 AM

PHP is suitable for web development, especially in rapid development and processing dynamic content, but is not good at data science and enterprise-level applications. Compared with Python, PHP has more advantages in web development, but is not as good as Python in the field of data science; compared with Java, PHP performs worse in enterprise-level applications, but is more flexible in web development; compared with JavaScript, PHP is more concise in back-end development, but is not as good as JavaScript in front-end development.

PHP vs. Python: Core Features and Functionality PHP vs. Python: Core Features and Functionality Apr 13, 2025 am 12:16 AM

PHP and Python each have their own advantages and are suitable for different scenarios. 1.PHP is suitable for web development and provides built-in web servers and rich function libraries. 2. Python is suitable for data science and machine learning, with concise syntax and a powerful standard library. When choosing, it should be decided based on project requirements.

PHP's Impact: Web Development and Beyond PHP's Impact: Web Development and Beyond Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

Java Program to Find the Volume of Capsule Java Program to Find the Volume of Capsule Feb 07, 2025 am 11:37 AM

Capsules are three-dimensional geometric figures, composed of a cylinder and a hemisphere at both ends. The volume of the capsule can be calculated by adding the volume of the cylinder and the volume of the hemisphere at both ends. This tutorial will discuss how to calculate the volume of a given capsule in Java using different methods. Capsule volume formula The formula for capsule volume is as follows: Capsule volume = Cylindrical volume Volume Two hemisphere volume in, r: The radius of the hemisphere. h: The height of the cylinder (excluding the hemisphere). Example 1 enter Radius = 5 units Height = 10 units Output Volume = 1570.8 cubic units explain Calculate volume using formula: Volume = π × r2 × h (4

PHP: The Foundation of Many Websites PHP: The Foundation of Many Websites Apr 13, 2025 am 12:07 AM

The reasons why PHP is the preferred technology stack for many websites include its ease of use, strong community support, and widespread use. 1) Easy to learn and use, suitable for beginners. 2) Have a huge developer community and rich resources. 3) Widely used in WordPress, Drupal and other platforms. 4) Integrate tightly with web servers to simplify development deployment.

See all articles