멀티 스레드 환경에서 Java 명령 재정렬을 해결하는 방법
1. 머리말
명령 재배치는 단일 스레드 환경에서 프로그램의 실행 효율성을 향상시키는 데 도움이 되며 다중 스레드 환경에서는 프로그램에 부정적인 영향을 미치지 않습니다. 명령 재정렬은 프로그램에 예상치 못한 오류를 가져옵니다. .
2. 문제 복구
(1) 연관 변수
다음은 명령어 재배열을 100% 복구할 수 있는 예입니다.
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; } } }
1. 결과 예측
reader
메서드는 flag
변수가 true인 경우에만 a
변수의 값을 콘솔에 출력합니다. . reader
方法仅在flag
变量为true时向控制台打印变量a
的值。
writer
方法先执行变量a
的赋值操作,后执行变量flag
的赋值操作。
如果按照上述分析逻辑,那么控制台打印的结果一定全为1。
2、指令重排
假如代码未发生指令重排,那么当flag
变量为true时,变量a
一定为1。
上述代码中关于变量a
和变量flag
在两个方法类均存在指令重排的情况。
public static void writer() { a = 1; flag = true; }
通过观察日志输出,发现有大量的0输出。
当writer
方法内部发生指令重排时,flag
变量先完成赋值,此时假如当前线程发生中断,其它线程在调用reader
方法,检测到flag
变量为true,那么便打印变量a
的值。此时控制台存在超出期望值的结果。
(二)new创建对象
使用关键字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; }
1、解析创建过程
使用关键字new创建一个对象,大致分为一下过程:
在栈空间创建引用地址
以类文件为模版在堆空间对象分配内存
成员变量初始化
使用构造函数初始化
将引用值赋值给左侧存储变量
2、重排序过程分析
针对上述示例,假设第一个线程进入synchronized代码块,并开始创建对象,由于重排序存在,正常的创建对象过程被打乱,可能会出现在栈空间创建引用地址后,将引用值赋值给左侧存储变量,随后因CPU调度时间片耗尽而产生中断的情况。
后续线程在检测到instance
writer
메소드는 먼저 a
변수의 할당 작업을 수행한 다음 flag
변수의 할당 작업을 수행합니다. 위의 분석 로직을 따르면 콘솔에서 출력되는 결과는 모두 1이어야 합니다. 2. 명령어 재배열코드에 명령어 재배열이 없으면 flag
변수가 true일 때 변수 a
는 1이어야 합니다. 위 코드에서는 a
변수와 flag
변수와 관련하여 두 메소드 클래스 모두에 명령 재배열이 있습니다. @Data @NoArgsConstructor @AllArgsConstructor public class ValueModel { private Integer value; private Boolean flag; }로그인 후 복사로그 출력을 관찰한 결과 0개의 출력이 많이 있는 것을 발견했습니다.
writer
메서드 내에서 명령을 재배치하면 flag
변수가 먼저 할당됩니다. 이때 현재 스레드가 중단되면 다른 스레드가 를 호출합니다. reader
메소드는 flag
변수가 true인지 감지한 다음 변수 a
의 값을 인쇄합니다. 이때 콘솔에서는 기대치를 초과하는 결과가 나오고 있습니다. (2) New는 객체를 생성합니다 new 키워드를 사용하여 객체를 생성하는 경우 비원자적 연산으로 인해 명령어 재배치가 발생하며 멀티 스레드 환경에서는 부정적인 영향을 미칩니다. 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));
}
}
}
로그인 후 복사
1. 생성 과정 분석new 키워드를 사용하여 객체를 생성하는데, 이는 대략 다음과 같은 과정으로 나뉩니다. 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)); } } }
- 스택 공간에 참조 주소 생성
- 클래스 사용 파일을 템플릿으로 만들어 힙 공간 메모리
🎜🎜2. 🎜🎜위의 예에서는 첫 번째 스레드가 동기화된 코드 블록을 입력하고 객체 생성을 시작한다고 가정합니다. 재정렬의 존재로 인해 스택 공간에 참조 주소가 생성된 후 정상적인 객체 생성 프로세스가 중단될 수 있습니다. , 왼쪽 저장 변수에 기준값을 할당한 후 CPU 스케줄링 시간으로 인해 칩 소모로 인해 인터럽트가 발생합니다. 🎜🎜후속 스레드에서왼쪽 저장소 변수에 참조 값 할당
인스턴스
변수가 비어 있지 않음을 감지하면 해당 변수가 직접 사용됩니다. 싱글톤 개체는 인스턴스화되지 않기 때문에 직접 사용하면 예상치 못한 결과가 발생합니다. 🎜🎜3. 명령어 재배열 대처🎜🎜 (1) AtomicReference 원자 클래스🎜🎜Atomic 클래스를 사용하여 관련 변수 집합을 객체로 캡슐화하고 원자 연산의 특성을 활용하여 명령어 재배열 문제를 효과적으로 방지합니다. 🎜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; }
위 내용은 멀티 스레드 환경에서 Java 명령 재정렬을 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Java의 Weka 가이드. 여기에서는 소개, weka java 사용 방법, 플랫폼 유형 및 장점을 예제와 함께 설명합니다.

Java의 Smith Number 가이드. 여기서는 정의, Java에서 스미스 번호를 확인하는 방법에 대해 논의합니다. 코드 구현의 예.

이 기사에서는 가장 많이 묻는 Java Spring 면접 질문과 자세한 답변을 보관했습니다. 그래야 면접에 합격할 수 있습니다.

Java 8은 스트림 API를 소개하여 데이터 컬렉션을 처리하는 강력하고 표현적인 방법을 제공합니다. 그러나 스트림을 사용할 때 일반적인 질문은 다음과 같은 것입니다. 기존 루프는 조기 중단 또는 반환을 허용하지만 스트림의 Foreach 메소드는이 방법을 직접 지원하지 않습니다. 이 기사는 이유를 설명하고 스트림 처리 시스템에서 조기 종료를 구현하기위한 대체 방법을 탐색합니다. 추가 읽기 : Java Stream API 개선 스트림 foreach를 이해하십시오 Foreach 메소드는 스트림의 각 요소에서 하나의 작업을 수행하는 터미널 작동입니다. 디자인 의도입니다

Java의 TimeStamp to Date 안내. 여기서는 소개와 예제와 함께 Java에서 타임스탬프를 날짜로 변환하는 방법에 대해서도 설명합니다.

캡슐은 3 차원 기하학적 그림이며, 양쪽 끝에 실린더와 반구로 구성됩니다. 캡슐의 부피는 실린더의 부피와 양쪽 끝에 반구의 부피를 첨가하여 계산할 수 있습니다. 이 튜토리얼은 다른 방법을 사용하여 Java에서 주어진 캡슐의 부피를 계산하는 방법에 대해 논의합니다. 캡슐 볼륨 공식 캡슐 볼륨에 대한 공식은 다음과 같습니다. 캡슐 부피 = 원통형 볼륨 2 반구 볼륨 안에, R : 반구의 반경. H : 실린더의 높이 (반구 제외). 예 1 입력하다 반경 = 5 단위 높이 = 10 단위 산출 볼륨 = 1570.8 입방 단위 설명하다 공식을 사용하여 볼륨 계산 : 부피 = π × r2 × h (4

Spring Boot는 강력하고 확장 가능하며 생산 가능한 Java 응용 프로그램의 생성을 단순화하여 Java 개발에 혁명을 일으킨다. Spring Ecosystem에 내재 된 "구성에 대한 협약"접근 방식은 수동 설정, Allo를 최소화합니다.
