목차
1. JMM이란
2. JMM은 무엇을 정의하나요?
원자성
가시성
순서
3. 8가지 유형의 메모리 상호 작용 작업
四、volatile关键字
可见性
volatile一定能保证线程安全吗
禁止指令重排序
volatile禁止指令重排序的原理
Java java지도 시간 Java의 JMM 높은 동시성 프로그래밍 예제 분석

Java의 JMM 높은 동시성 프로그래밍 예제 분석

May 02, 2023 pm 06:52 PM
java jmm

1. JMM이란

JMM은 Java 메모리 모델입니다. 하드웨어 제조업체와 운영 체제에 따라 메모리 액세스에 특정 차이가 있기 때문에 동일한 코드가 다른 시스템에서 실행될 때 다양한 문제가 발생합니다. 따라서 JMM(Java 메모리 모델)은 다양한 하드웨어 및 운영 체제의 메모리 액세스 차이를 보호하여 다양한 플랫폼에서 Java 프로그램에 대한 일관된 동시성 효과를 달성합니다.

Java 메모리 모델은 인스턴스 변수와 정적 변수를 포함한 모든 변수가 메인 메모리에 저장되지만 로컬 변수와 메소드 매개변수는 포함되지 않는다고 규정합니다. 각 스레드에는 자체 작업 메모리가 있습니다. 스레드의 작업 메모리는 스레드에서 사용하는 변수를 저장하며 변수에 대한 스레드의 작업은 모두 작업 메모리에서 수행됩니다. 스레드는 주 메모리에서 변수를 직접 읽거나 쓸 수 없습니다.

서로 다른 스레드는 서로의 작업 메모리에 있는 변수에 액세스할 수 없습니다. 스레드 간의 변수값 전달은 메인 메모리를 통해 완료되어야 합니다.

Java의 JMM 높은 동시성 프로그래밍 예제 분석

각 스레드의 작업 메모리는 독립적입니다. 스레드 작업 데이터는 작업 메모리에서만 수행된 다음 기본 메모리로 다시 플러시될 수 있습니다. 이는 Java 메모리 모델에 정의된 대로 스레드가 작동하는 기본 방식입니다.

여기 있는 일부 사람들은 Java 메모리 모델을 Java 메모리 구조로 오해하고 힙, 스택, GC 가비지 수집에 대해 답변할 것이며 결국 면접관이 묻고 싶은 질문과 거리가 멀다는 점을 알려드립니다. 실제로 Java 메모리 모델에 관해 질문을 받으면 일반적으로 멀티스레딩 및 Java 동시성과 관련된 질문을 하고 싶어합니다.

2. JMM은 무엇을 정의하나요?

이것은 간단합니다. 전체 Java 메모리 모델은 실제로 세 가지 특성을 중심으로 구축됩니다. 그것은 원자성, 가시성, 질서입니다. 이 세 가지 특성은 전체 Java 동시성의 기초라고 할 수 있습니다.

원자성

원자성은 작업이 분할 불가능하고 중단할 수 없으며 스레드가 실행 중에 다른 스레드의 방해를 받지 않음을 의미합니다.

면접관은 펜을 들고 코드를 작성했습니다. 다음 코드는 원자성을 보장할 수 있나요?

int i = 2;
int j = i;
i++;
i = i + 1;
로그인 후 복사

첫 번째 문장은 기본 유형 할당 연산으로, 원자 연산이어야 합니다.

두 번째 문장은 i의 값을 먼저 읽은 다음 이를 j에 할당합니다. 이 2단계 연산은 원자성을 보장할 수 없습니다.

세 번째와 네 번째 문장은 실제로 동일합니다. 먼저 i 값을 읽은 다음 +1하고 마지막으로 i에 값을 할당합니다. 이는 3단계 작업이므로 원자성을 보장할 수 없습니다.

JMM은 코드 블록의 원자성을 보장하려는 경우 동기화 키워드인 monitorenter 및 moniterexit라는 두 가지 바이트코드 명령어를 제공합니다. 따라서 동기화된 블록 간의 작업은 원자적입니다.

가시성

가시성은 한 스레드가 공유 변수의 값을 수정하면 다른 스레드가 해당 값이 수정되었음을 즉시 알 수 있음을 의미합니다. Java는 가시성을 제공하기 위해 휘발성 키워드를 사용합니다. 변수가 휘발성으로 수정되면 해당 변수는 수정된 후 즉시 주 메모리로 플러시됩니다. 다른 스레드가 변수를 읽어야 할 경우 주 메모리에서 새 값을 읽습니다. 일반 변수는 이를 보장할 수 없습니다.

휘발성 키워드 외에도 최종 및 동기화도 가시성을 얻을 수 있습니다.

동기화의 원칙은 실행 후 잠금 해제에 들어가기 전에 공유 변수를 메인 메모리에 동기화해야 한다는 것입니다.

최종 수정 필드는 초기화가 완료되면 개체가 이스케이프되지 않으면 다른 스레드에 표시됩니다(즉, 초기화가 완료된 후 다른 스레드에서 개체를 사용할 수 있음을 의미).

순서

Java에서는 동기화 또는 휘발성을 사용하여 여러 스레드 간의 작업 순서를 보장할 수 있습니다. 구현 원칙에는 몇 가지 차이점이 있습니다.

휘발성 키워드는 질서를 보장하기 위해 메모리 장벽을 사용하여 명령 재정렬을 금지합니다.

동기화의 원칙은 스레드가 잠긴 후 다른 스레드가 다시 잠기기 전에 잠금을 해제해야 동기화로 래핑된 코드 블록이 여러 스레드 간에 순차적으로 실행된다는 것입니다.

3. 8가지 유형의 메모리 상호 작용 작업

8가지 유형의 메모리 상호 작용 작업이 있습니다.

  • lock(잠금)은 주 메모리의 변수에 대해 작동하고 변수를 스레드 독점으로 표시합니다.

  • read(읽기)는 주 메모리의 변수에 작용하고 다음 로드 작업에 사용하기 위해 주 메모리에서 스레드의 작업 메모리로 변수 값을 전송합니다.

  • load(로딩)는 작업 메모리의 변수에 작용하며 읽기 작업의 주 메모리 변수를 작업 메모리의 변수 복사본에 넣습니다.

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

Java의 JMM 높은 동시성 프로그래밍 예제 분석

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++;
}
}
}
로그인 후 복사

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

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

private static synchronized void add() {
count++;
}
로그인 후 복사

禁止指令重排序

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

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

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

Java의 JMM 높은 동시성 프로그래밍 예제 분석

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

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

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
//...
}
로그인 후 복사

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

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

volatile禁止指令重排序的原理

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

  • LoadLoad 장벽: Load1, LoadLoad, Load2와 같은 명령문용. Load2에서 읽을 데이터와 후속 읽기 작업에 액세스하기 전에 Load1에서 읽을 데이터를 읽었다는 것이 보장됩니다.

  • StoreStore 장벽: Store2 및 후속 쓰기 작업이 실행되기 전의 Store1, StoreStore, Store2 명령문의 경우 Store1의 쓰기 작업이 다른 프로세서에 표시된다는 것이 보장됩니다.

  • LoadStore 장벽: Load1, LoadStore 및 Store2와 같은 문의 경우 Store2 및 후속 쓰기 작업이 플러시되기 전에 Load1에서 읽을 데이터를 읽어야 합니다.

  • StoreLoad 장벽: Store1, StoreLoad 및 Load2와 같은 문의 경우 Load2 및 모든 후속 읽기 작업이 실행되기 전에 Store1에 대한 쓰기가 모든 프로세서에 표시되도록 보장됩니다.

각 휘발성 읽기 작업 후에 LoadLoad 장벽을 삽입하고 읽기 작업 후에 LoadStore 장벽을 삽입합니다.

Java의 JMM 높은 동시성 프로그래밍 예제 분석

각 휘발성 쓰기 작업 앞에 StoreStore 장벽을 삽입하고 뒷면에 ​​SotreLoad 장벽을 삽입합니다.

Java의 JMM 높은 동시성 프로그래밍 예제 분석

위 내용은 Java의 JMM 높은 동시성 프로그래밍 예제 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

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

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

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

Java 8 Stream foreach에서 나누거나 돌아 오시겠습니까? Java 8 Stream foreach에서 나누거나 돌아 오시겠습니까? Feb 07, 2025 pm 12:09 PM

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

PHP : 웹 개발의 핵심 언어 PHP : 웹 개발의 핵심 언어 Apr 13, 2025 am 12:08 AM

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 ​​있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

PHP vs. Python : 차이점 이해 PHP vs. Python : 차이점 이해 Apr 11, 2025 am 12:15 AM

PHP와 Python은 각각 고유 한 장점이 있으며 선택은 프로젝트 요구 사항을 기반으로해야합니다. 1.PHP는 간단한 구문과 높은 실행 효율로 웹 개발에 적합합니다. 2. Python은 간결한 구문 및 풍부한 라이브러리를 갖춘 데이터 과학 및 기계 학습에 적합합니다.

PHP 대 기타 언어 : 비교 PHP 대 기타 언어 : 비교 Apr 13, 2025 am 12:19 AM

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP vs. Python : 핵심 기능 및 기능 PHP vs. Python : 핵심 기능 및 기능 Apr 13, 2025 am 12:16 AM

PHP와 Python은 각각 고유 한 장점이 있으며 다양한 시나리오에 적합합니다. 1.PHP는 웹 개발에 적합하며 내장 웹 서버 및 풍부한 기능 라이브러리를 제공합니다. 2. Python은 간결한 구문과 강력한 표준 라이브러리가있는 데이터 과학 및 기계 학습에 적합합니다. 선택할 때 프로젝트 요구 사항에 따라 결정해야합니다.

캡슐의 양을 찾기위한 Java 프로그램 캡슐의 양을 찾기위한 Java 프로그램 Feb 07, 2025 am 11:37 AM

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

PHP의 영향 : 웹 개발 및 그 이상 PHP의 영향 : 웹 개발 및 그 이상 Apr 18, 2025 am 12:10 AM

phphassignificallyimpactedwebdevelopmentandextendsbeyondit

PHP : 많은 웹 사이트의 기초 PHP : 많은 웹 사이트의 기초 Apr 13, 2025 am 12:07 AM

PHP가 많은 웹 사이트에서 선호되는 기술 스택 인 이유에는 사용 편의성, 강력한 커뮤니티 지원 및 광범위한 사용이 포함됩니다. 1) 배우고 사용하기 쉽고 초보자에게 적합합니다. 2) 거대한 개발자 커뮤니티와 풍부한 자원이 있습니다. 3) WordPress, Drupal 및 기타 플랫폼에서 널리 사용됩니다. 4) 웹 서버와 밀접하게 통합하여 개발 배포를 단순화합니다.

See all articles