Heim >Java >javaLernprogramm >So lösen Sie die Neuordnung von Java-Anweisungen in einer Multithread-Umgebung
Die Neuanordnung von Anweisungen trägt dazu bei, die Ausführungseffizienz des Programms in einer Single-Thread-Umgebung zu verbessern, und hat keine negativen Auswirkungen auf das Programm; in einer Multi-Thread-Umgebung führt die Neuanordnung von Anweisungen zu unerwarteten Fehlern im Programm .
Das Folgende ist ein Beispiel, mit dem die Neuanordnung von Anweisungen zu 100 % wiederhergestellt werden kann.
public class D { static Integer a; static Boolean flag; public static void writer() { a = 1; flag = true; } public static void reader() { if (flag != null && flag) { System.out.println(a); a = 0; flag = false; } } }
Die Methode reader
gibt den Wert der Variablen a
nur dann auf der Konsole aus, wenn die Variable flag
wahr ist . reader
方法仅在flag
变量为true时向控制台打印变量a
的值。
writer
方法先执行变量a
的赋值操作,后执行变量flag
的赋值操作。
如果按照上述分析逻辑,那么控制台打印的结果一定全为1。
假如代码未发生指令重排,那么当flag
变量为true时,变量a
一定为1。
上述代码中关于变量a
和变量flag
在两个方法类均存在指令重排的情况。
public static void writer() { a = 1; flag = true; }
通过观察日志输出,发现有大量的0输出。
当writer
方法内部发生指令重排时,flag
变量先完成赋值,此时假如当前线程发生中断,其它线程在调用reader
方法,检测到flag
变量为true,那么便打印变量a
的值。此时控制台存在超出期望值的结果。
使用关键字new创建对象时,因其非原子操作,故存在指令重排,指令重排在多线程环境下会带来负面影响。
public class Singleton { private static UserModel instance; public static UserModel getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new UserModel(2, "B"); } } } return instance; } } @Data @AllArgsConstructor class UserModel { private Integer userId; private String userName; }
使用关键字new创建一个对象,大致分为一下过程:
在栈空间创建引用地址
以类文件为模版在堆空间对象分配内存
成员变量初始化
使用构造函数初始化
将引用值赋值给左侧存储变量
针对上述示例,假设第一个线程进入synchronized代码块,并开始创建对象,由于重排序存在,正常的创建对象过程被打乱,可能会出现在栈空间创建引用地址后,将引用值赋值给左侧存储变量,随后因CPU调度时间片耗尽而产生中断的情况。
后续线程在检测到instance
writer
-Methode führt zuerst die Zuweisungsoperation der Variablen a
und dann die Zuweisungsoperation der Variablen flag
durch. Wenn Sie der obigen Analyselogik folgen, müssen die von der Konsole gedruckten Ergebnisse alle 1 sein. 2. BefehlsumordnungWenn der Code keine Befehlsumordnung hat, muss die Variable a
1 sein, wenn die Variable flag
wahr ist. Im obigen Code gibt es in beiden Methodenklassen eine Befehlsumordnung bezüglich der Variablen a
und der Variablen flag
. Wenn die Anweisungen innerhalb der@Data @NoArgsConstructor @AllArgsConstructor public class ValueModel { private Integer value; private Boolean flag; }Beim Beobachten der Protokollausgabe haben wir festgestellt, dass es eine große Anzahl von 0-Ausgaben gibt.
writer
-Methode neu angeordnet werden, wird zuerst die Variable flag
zugewiesen. Wenn der aktuelle Thread zu diesem Zeitpunkt unterbrochen wird, rufen andere Threads auf Die Methode „reader code>“ erkennt, dass die Variable <code>flag
wahr ist, und gibt dann den Wert der Variablen a
aus. Zu diesem Zeitpunkt gibt es in der Konsole Ergebnisse, die den erwarteten Wert überschreiten. (2) Neu erstellt ObjekteBei Verwendung des Schlüsselworts new zum Erstellen eines Objekts kommt es aufgrund der nicht-atomaren Operation zu einer Neuanordnung der Anweisungen, die in einer Multithread-Umgebung negative Auswirkungen hat. public class E { private static final AtomicReference<ValueModel> ar = new AtomicReference<>(new ValueModel()); public static void writer() { ar.set(new ValueModel(1, true)); } public static void reader() { ValueModel valueModel = ar.get(); if (valueModel.getFlag() != null && valueModel.getFlag()) { System.out.println(valueModel.getValue()); ar.set(new ValueModel(0, false)); } } }
instance
nicht leer ist, wird sie direkt verwendet. Da Singleton-Objekte nicht instanziiert werden, führt ihre direkte Verwendung zu unerwarteten Ergebnissen. public class Singleton { private volatile static UserModel instance; public static UserModel getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new UserModel(2, "B"); } } } return instance; } } @Data @AllArgsConstructor class UserModel { private Integer userId; private String userName; }Die Atomklasse sollte die erste Wahl sein, um das Problem der Befehlsumordnung in einer Multithread-Umgebung zu lösen. Sie ist nicht nur leicht zu verstehen, sondern auch die zwischen Threads verwendete nicht schwere Mutex-Sperre ist relativ effizient.
rrreeeWenn Anweisungen für eine Gruppe verwandter Variablen neu angeordnet werden, ist die Verwendung der atomaren Operationsklasse eine bessere Lösung.🎜🎜 (2) flüchtiges Schlüsselwort 🎜rrreee🎜 4. Verständnis der Befehlsumordnung 🎜🎜1. Befehlsumordnung ist nicht auf Java-Programme beschränkt. Tatsächlich gibt es bei verschiedenen Compilern Befehlsumlagerungen, die von der Software bis zur CPU reichen Hardware. Die Neuanordnung von Anweisungen ist eine Leistungsoptimierung für Single-Thread-Programme. Es sollte klar sein, dass die Neuanordnung von Anweisungen die erwarteten Ergebnisse der sequentiellen Programmausführung in einer Single-Thread-Umgebung nicht verändert. 🎜🎜2. Befehlsumordnung in Multithread-Umgebungen🎜🎜Die oben genannten Umstellungen von Befehlen in zwei typischen Multithread-Umgebungen wurden analysiert, ihre negativen Auswirkungen wurden analysiert und entsprechende Gegenmaßnahmen bereitgestellt. 🎜🎜🎜🎜Für zugehörige Variablen kapseln Sie sie zunächst in ein Objekt und verwenden Sie dann atomare Klassen zum Betrieb. 🎜🎜🎜🎜Für neue Objekte verwenden Sie das Schlüsselwort volatile, um das Zielobjekt zu ändern. 🎜🎜🎜🎜3 Synchronisierte Sperren haben nichts zu tun mit der Neuordnung tun🎜 🎜Synchronisierte Sperre stellt sicher, dass Threads durch Mutex-Sperren geordnet auf bestimmte Codeblöcke zugreifen. Der Code innerhalb des Codeblocks wird normalerweise entsprechend der vom Compiler implementierten Strategie neu angeordnet. 🎜🎜Obwohl synchronisierte Sperren die nachteiligen Auswirkungen einer Neuordnung in einer Multithread-Umgebung vermeiden können, ist der durch Mutex-Sperren verursachte Thread-Overhead relativ groß und wird nicht empfohlen. 🎜🎜🎜Nichtatomare Operationen in synchronisierten Blöcken können immer noch zu einer Neuordnung der Befehle führen🎜🎜
Das obige ist der detaillierte Inhalt vonSo lösen Sie die Neuordnung von Java-Anweisungen in einer Multithread-Umgebung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!