Inhaltsverzeichnis
1. Was ist JMM
2. Was definiert JMM?
Atomizität
Sichtbarkeit
Ordnung
Drei und acht Arten von Speicherinteraktionsoperationen
四、volatile关键字
可见性
volatile一定能保证线程安全吗
禁止指令重排序
volatile禁止指令重排序的原理
Heim Java javaLernprogramm Analyse von JMM-Programmierbeispielen mit hoher Parallelität in Java

Analyse von JMM-Programmierbeispielen mit hoher Parallelität in Java

May 02, 2023 pm 06:52 PM
java jmm

1. Was ist JMM

JMM ist das Java-Speichermodell (Java-Speichermodell). Da es bei verschiedenen Hardwareherstellern und Betriebssystemen gewisse Unterschiede beim Speicherzugriff gibt, treten verschiedene Probleme auf, wenn derselbe Code auf verschiedenen Systemen ausgeführt wird. Daher schützt das Java Memory Model (JMM) die Speicherzugriffsunterschiede verschiedener Hardware und Betriebssysteme, um konsistente Parallelitätseffekte für Java-Programme auf verschiedenen Plattformen zu erzielen.

Das Java-Speichermodell schreibt vor, dass alle Variablen im Hauptspeicher gespeichert werden, einschließlich Instanzvariablen und statischen Variablen, jedoch keine lokalen Variablen und Methodenparameter. Jeder Thread verfügt über einen eigenen Arbeitsspeicher. Der Arbeitsspeicher des Threads speichert die vom Thread verwendeten Variablen und eine Kopie des Hauptspeichers. Alle Operationen des Threads an Variablen werden im Arbeitsspeicher ausgeführt. Threads können Variablen im Hauptspeicher nicht direkt lesen oder schreiben.

Verschiedene Threads können nicht auf Variablen im Arbeitsspeicher des anderen zugreifen. Die Übertragung variabler Werte zwischen Threads muss über den Hauptspeicher erfolgen.

Analyse von JMM-Programmierbeispielen mit hoher Parallelität in Java

Die Thread-Operationsdaten können nur im Arbeitsspeicher ausgeführt und dann zurück in den Hauptspeicher geleert werden. Dies ist die grundlegende Funktionsweise von Threads, wie sie im Java-Speichermodell definiert ist.

Eine herzliche Erinnerung: Einige Leute hier werden das Java-Speichermodell als Java-Speicherstruktur missverstehen und dann mit Heap, Stack und GC-Garbage Collection antworten, und schließlich ist es weit von der Frage entfernt, die der Interviewer stellen möchte fragen. Wenn sie nach dem Java-Speichermodell gefragt werden, möchten sie im Allgemeinen Fragen zu Multithreading und Java-Parallelität stellen.

2. Was definiert JMM?

Das gesamte Java-Speichermodell basiert eigentlich auf drei Merkmalen. Sie sind: Atomizität, Sichtbarkeit und Ordnung. Man kann sagen, dass diese drei Merkmale die Grundlage der gesamten Java-Parallelität bilden.

Atomizität

Atomizität bedeutet, dass eine Operation unteilbar und ununterbrochen ist und ein Thread während der Ausführung nicht durch andere Threads gestört wird.

Der Interviewer nahm einen Stift und schrieb einen Code. Können die folgenden Codezeilen Atomizität garantieren?

int i = 2;
int j = i;
i++;
i = i + 1;
Nach dem Login kopieren

Der erste Satz ist eine grundlegende Typzuweisungsoperation, bei der es sich um eine atomare Operation handeln muss.

Der zweite Satz liest zuerst den Wert von i und weist ihn dann j zu. Diese zweistufige Operation kann keine Atomizität garantieren.

Der dritte und vierte Satz sind tatsächlich gleichwertig. Lesen Sie zuerst den Wert von i, dann +1 und weisen Sie ihn schließlich zu. Es handelt sich um eine dreistufige Operation, die keine Atomizität garantieren kann.

JMM kann nur die grundlegende Atomizität garantieren. Wenn Sie die Atomizität eines Codeblocks sicherstellen möchten, stellt es zwei Bytecode-Anweisungen bereit, Monitorenter und Monitorexit, die das synchronisierte Schlüsselwort sind. Daher sind Operationen zwischen synchronisierten Blöcken atomar.

Sichtbarkeit

Sichtbarkeit bedeutet, dass andere Threads sofort erkennen können, dass der Wert einer gemeinsam genutzten Variablen geändert wurde, wenn ein Thread ihn ändert. Java verwendet das Schlüsselwort volatile, um Sichtbarkeit bereitzustellen. Wenn eine Variable flüchtig geändert wird, wird die Variable sofort nach der Änderung in den Hauptspeicher geleert. Wenn andere Threads die Variable lesen müssen, lesen sie den neuen Wert aus dem Hauptspeicher. Gewöhnliche Variablen können dies nicht garantieren.

Neben dem volatilen Schlüsselwort können auch final und synchronisiert Sichtbarkeit erreichen.

Das Prinzip der Synchronisierung besteht darin, dass die gemeinsam genutzten Variablen nach der Ausführung mit dem Hauptspeicher synchronisiert werden müssen, bevor sie entsperrt werden.

Endgültig geänderte Felder sind nach der Initialisierung für andere Threads sichtbar, wenn kein Objekt entkommt (was bedeutet, dass das Objekt nach Abschluss der Initialisierung von anderen Threads verwendet werden kann).

Ordnung

In Java können Sie synchronisiert oder flüchtig verwenden, um die Ordnung von Vorgängen zwischen mehreren Threads sicherzustellen. Es gibt einige Unterschiede in den Implementierungsprinzipien: Das Schlüsselwort

volatile verwendet Speicherbarrieren, um die Neuordnung von Anweisungen zu verhindern und die Ordnung sicherzustellen.

Das Prinzip der Synchronisierung besteht darin, dass ein Thread nach dem Sperren entsperrt werden muss, bevor andere Threads erneut gesperrt werden können, sodass die von der Synchronisierung umschlossenen Codeblöcke seriell zwischen mehreren Threads ausgeführt werden.

Drei und acht Arten von Speicherinteraktionsoperationen

Es gibt 8 Arten von Speicherinteraktionsoperationen:

  • sperren (lock) wirkt auf Variablen im Hauptspeicher und markiert Variablen als Thread-exklusive Zustände.

  • lesen (lesen), wirkt auf Variablen im Hauptspeicher und überträgt den Wert der Variablen für den nächsten Ladevorgang vom Hauptspeicher in den Arbeitsspeicher des Threads verwenden.

  • laden (laden), wirkt auf die Variablen des Arbeitsspeichers und legt die Variablen des Lesevorgangs im Hauptspeicher in die Variablenkopie des Arbeitsspeichers ab.

  • verwenden (verwenden), wirken auf Variablen im Arbeitsspeicher, übertragen die Variablen im Arbeitsspeicher an die Ausführungsmaschine, wann immer die virtuelle Maschine auf eine Variable trifft, die benötigt wird zu verwenden. Diese Operation wird ausgeführt, wenn die Bytecode-Anweisung des Werts angegeben wird.

  • assign (Zuweisung), die auf eine Variable im Arbeitsspeicher einwirkt. Sie weist einen von der Ausführungs-Engine empfangenen Wert einer Kopie der Variablen im Arbeitsspeicher zu . Wann immer Die virtuelle Maschine führt diesen Vorgang aus, wenn sie auf eine Bytecode-Anweisung stößt, die einer Variablen einen Wert zuweist.

  • store (Speicher), eine Variable, die auf den Arbeitsspeicher einwirkt. Sie überträgt den Wert einer Variablen aus dem Arbeitsspeicher in den Hauptspeicher zur späteren Schreibverwendung. .

  • 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保证可见性的流程大概就是这个一个过程:

Analyse von JMM-Programmierbeispielen mit hoher Parallelität 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++;
}
}
}
Nach dem Login kopieren

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

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

private static synchronized void add() {
count++;
}
Nach dem Login kopieren

禁止指令重排序

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

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

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

Analyse von JMM-Programmierbeispielen mit hoher Parallelität 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
//...
}
Nach dem Login kopieren

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

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

volatile禁止指令重排序的原理

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

  • LoadLoad-Barriere: Für Anweisungen wie Load1, LoadLoad, Load2. Vor dem Zugriff auf die zu lesenden Daten in Load2 und nachfolgenden Lesevorgängen wird sichergestellt, dass die zu lesenden Daten in Load1 gelesen wurden.

  • StoreStore-Barriere: Stellen Sie bei solchen Anweisungen Store1, StoreStore, Store2 sicher, dass der Schreibvorgang von Store1 für andere Prozessoren sichtbar ist, bevor Store2 und nachfolgende Schreibvorgänge ausgeführt werden.

  • LoadStore-Barriere: Stellen Sie bei Anweisungen wie Load1, LoadStore, Store2 sicher, dass die von Load1 zu lesenden Daten gelesen werden, bevor Store2 und nachfolgende Schreibvorgänge gelöscht werden. vollständig.

  • StoreLoad-Barriere: Stellen Sie bei solchen Anweisungen Store1, StoreLoad, Load2 sicher, dass der Schreibvorgang in Store1 für alle Prozessoren sichtbar ist, bevor Load2 und alle nachfolgenden Lesevorgänge ausgeführt werden.

Fügen Sie nach jedem flüchtigen Lesevorgang eine LoadLoad-Barriere und nach einem Lesevorgang eine LoadStore-Barriere ein.

Analyse von JMM-Programmierbeispielen mit hoher Parallelität in Java

Fügen Sie vor jedem flüchtigen Schreibvorgang eine StoreStore-Barriere und hinten eine SotreLoad-Barriere ein.

Analyse von JMM-Programmierbeispielen mit hoher Parallelität in Java

Das obige ist der detaillierte Inhalt vonAnalyse von JMM-Programmierbeispielen mit hoher Parallelität in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
2 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Repo: Wie man Teamkollegen wiederbelebt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Abenteuer: Wie man riesige Samen bekommt
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Quadratwurzel in Java Quadratwurzel in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Quadratwurzel in Java. Hier diskutieren wir anhand eines Beispiels und seiner Code-Implementierung, wie Quadratwurzel in Java funktioniert.

Perfekte Zahl in Java Perfekte Zahl in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur perfekten Zahl in Java. Hier besprechen wir die Definition, Wie prüft man die perfekte Zahl in Java?, Beispiele mit Code-Implementierung.

Zufallszahlengenerator in Java Zufallszahlengenerator in Java Aug 30, 2024 pm 04:27 PM

Leitfaden zum Zufallszahlengenerator in Java. Hier besprechen wir Funktionen in Java anhand von Beispielen und zwei verschiedene Generatoren anhand ihrer Beispiele.

Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

Leitfaden für Weka in Java. Hier besprechen wir die Einführung, die Verwendung von Weka Java, die Art der Plattform und die Vorteile anhand von Beispielen.

Armstrong-Zahl in Java Armstrong-Zahl in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Armstrong-Zahl in Java. Hier besprechen wir eine Einführung in die Armstrong-Zahl in Java zusammen mit einem Teil des Codes.

Smith-Nummer in Java Smith-Nummer in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur Smith-Zahl in Java. Hier besprechen wir die Definition: Wie überprüft man die Smith-Nummer in Java? Beispiel mit Code-Implementierung.

Fragen zum Java Spring-Interview Fragen zum Java Spring-Interview Aug 30, 2024 pm 04:29 PM

In diesem Artikel haben wir die am häufigsten gestellten Fragen zu Java Spring-Interviews mit ihren detaillierten Antworten zusammengestellt. Damit Sie das Interview knacken können.

Brechen oder aus Java 8 Stream foreach zurückkehren? Brechen oder aus Java 8 Stream foreach zurückkehren? Feb 07, 2025 pm 12:09 PM

Java 8 führt die Stream -API ein und bietet eine leistungsstarke und ausdrucksstarke Möglichkeit, Datensammlungen zu verarbeiten. Eine häufige Frage bei der Verwendung von Stream lautet jedoch: Wie kann man von einem Foreach -Betrieb brechen oder zurückkehren? Herkömmliche Schleifen ermöglichen eine frühzeitige Unterbrechung oder Rückkehr, aber die Stream's foreach -Methode unterstützt diese Methode nicht direkt. In diesem Artikel werden die Gründe erläutert und alternative Methoden zur Implementierung vorzeitiger Beendigung in Strahlverarbeitungssystemen erforscht. Weitere Lektüre: Java Stream API -Verbesserungen Stream foreach verstehen Die Foreach -Methode ist ein Terminalbetrieb, der einen Vorgang für jedes Element im Stream ausführt. Seine Designabsicht ist

See all articles