데이터 경합 및 순차 일관성 보장프로그램이 올바르게 동기화되지 않으면 데이터 경합이 존재합니다. Java 메모리 모델 사양에서는 데이터 경쟁을 다음과 같이 정의합니다. 한 스레드에서 변수에 쓰기 다른 스레드에서 동일한 변수 읽기 쓰기와 읽기는 동기화 순서가 아닙니다. 코드에 데이터 경합이 포함된 경우 프로그램 실행은 종종 직관에 반하는 결과를 생성합니다(이전 장의 예의 경우처럼). 다중 스레드 프로그램이 올바르게 동기화되면 해당 프로그램은 데이터 경합이 없는 프로그램이 됩니다. JMM은 올바르게 동기화된 멀티스레드 프로그램의 메모리 일관성을 위해 다음과 같이 보장합니다. 프로그램이 올바르게 동기화되면 프로그램 실행이 순차적으로 일관됩니다. 프로그램의 순차적 일관성이 있을 것입니다. 결과는 프로그램이 순차적으로 일관된 메모리 모델에서 실행된 것과 동일합니다(곧 살펴보겠지만 이는 프로그래머에게 매우 강력한 보장입니다). 여기서 동기화는 일반적인 동기화 기본 요소(잠금, 휘발성 및 최종)의 올바른 사용을 포함하여 넓은 의미의 동기화를 의미합니다. 순차 일관성 메모리 모델순차 일관성 메모리 모델은 컴퓨터 과학자들이 이상화한 이론적 참조 모델로, 프로그래머에게 강력한 메모리 가시성을 보장합니다. 순차 일관성 메모리 모델에는 두 가지 주요 특징이 있습니다. 스레드의 모든 작업은 프로그램 순서대로 실행되어야 합니다. (프로그램 동기화 여부에 관계없이) 모든 스레드는 단일 작업 실행 순서만 볼 수 있습니다. 순차적으로 일관된 메모리 모델에서 모든 작업은 원자적으로 실행되어야 하며 모든 스레드에 즉시 표시되어야 합니다. 순차 일관성 모델은 프로그래머에게 다음과 같은 보기를 제공합니다. 개념적으로 순차 일관성 모델에는 단일 전역 메모리가 있습니다. 왼쪽과 오른쪽으로 흔들리는 스위치를 통해 모든 스레드에 연결됩니다. 동시에 각 스레드는 프로그램 순서대로 메모리 읽기/쓰기 작업을 수행해야 합니다. 위 그림에서 우리는 언제든지 최대 하나의 스레드가 메모리에 연결될 수 있음을 알 수 있습니다. 여러 스레드가 동시에 실행되는 경우 그림의 스위칭 장치는 모든 스레드의 모든 메모리 읽기/쓰기 작업을 직렬화할 수 있습니다. 더 나은 이해를 위해 아래에서는 두 개의 도식 다이어그램을 사용하여 순차 일관성 모델의 특성을 자세히 설명합니다. 두 개의 스레드 A와 B가 동시에 실행되고 있다고 가정합니다. 스레드 A에는 세 가지 작업이 있으며 프로그램의 순서는 A1->A2->A3입니다. 스레드 B에도 세 가지 작업이 있으며 프로그램에서의 순서는 B1->B2->B3입니다. 두 스레드가 모니터를 사용하여 올바르게 동기화한다고 가정합니다. 스레드 A는 세 가지 작업이 수행된 후 모니터를 해제하고 이후 스레드 B는 동일한 모니터를 획득합니다. 그러면 순차 일관성 모델에서 프로그램의 실행 효과는 아래 그림과 같습니다. 이제 두 스레드가 동기화되지 않았다고 가정해 보겠습니다. 비동기화 프로그램 순차 일관성 모델의 실행 도식: 비동기화 프로그램 순차 일관성 모델에서 전체 실행 순서는 순서가 없지만 모든 스레드는 일관된 전체 실행 순서. 위 그림을 예로 들면, 스레드 A와 B의 실행 순서는 B1->A1->A2->B2->A3->B3입니다. 이러한 보장은 순차적으로 일관된 메모리 모델의 모든 작업이 모든 스레드에 즉시 표시되어야 하기 때문에 달성됩니다. 그러나 JMM에서는 그런 보장이 없습니다. JMM에서는 동기화되지 않은 프로그램의 전체 실행 순서가 틀릴 뿐만 아니라, 모든 스레드에서 보는 작업 실행 순서도 일치하지 않을 수 있습니다. 예를 들어, 현재 스레드가 로컬 메모리에 기록된 데이터를 캐시하고 이를 주 메모리로 새로 고치지 않은 경우 쓰기 작업은 다른 스레드의 관점에서 현재 스레드에만 표시되며 쓰기 작업은 다음과 같이 간주됩니다. 작업이 전혀 발생하지 않았습니다. 현재 스레드에 의해 실행되었습니다. 현재 스레드가 로컬 메모리에 기록된 데이터를 주 메모리로 플러시한 후에만 이 쓰기 작업을 다른 스레드에서 볼 수 있습니다. 이 경우 작업이 수행되는 순서는 현재 스레드와 다른 스레드 간에 일치하지 않습니다. 동기화된 프로그램의 순차 일관성 효과 아래에서는 모니터를 사용하여 이전 샘플 프로그램 ReorderExample을 동기화하여 올바르게 동기화된 프로그램이 어떻게 순차 일관성을 갖는지 확인합니다. 다음 샘플 코드를 살펴보세요. 위 샘플 코드에서는 스레드 A가writer() 메서드를 실행한 후 스레드 B가 reader() 메서드를 실행한다고 가정합니다. 방법. 이것은 적절하게 동기화된 멀티스레드 프로그램입니다. JMM 사양에 따르면 이 프로그램의 실행 결과는 순차 일관성 모델에서 이 프로그램의 실행 결과와 동일합니다. 다음은 두 가지 메모리 모델의 프로그램 실행 타이밍 비교 차트입니다. 순차적 일관성 모델에서는 모든 작업이 프로그램 순서에 따라 순차적으로 실행됩니다. JMM에서는 중요 섹션의 코드를 다시 정렬할 수 있습니다(그러나 JMM은 중요 섹션의 코드가 중요 섹션 외부로 "이스케이프"되는 것을 허용하지 않으므로 모니터의 의미가 파괴됩니다). JMM은 모니터를 종료하고 모니터에 들어가는 두 가지 주요 시점에서 몇 가지 특별한 처리를 수행하여 스레드가 이 두 시점에서 순차 일관성 모델과 동일한 메모리 보기를 갖도록 합니다(구체적인 세부 사항은 나중에 설명됩니다). 스레드 A가 임계 섹션에서 재정렬되었지만 모니터의 상호 배타적 실행 특성으로 인해 여기서 스레드 B는 임계 섹션에서 스레드 A의 재정렬을 "관찰"할 수 없습니다. 이러한 재정렬은 프로그램의 실행 결과를 변경하지 않고 실행 효율성을 향상시킵니다. 여기에서 특정 구현에 대한 JMM의 기본 정책을 볼 수 있습니다. 즉, (올바르게 동기화된) 프로그램 실행 결과를 변경하지 않고 컴파일러 및 프로세서 최적화를 위해 최대한 많은 편의를 제공합니다. 동기화되지 않은 프로그램의 실행 특성동기화되지 않거나 잘못 동기화된 멀티 스레드 프로그램의 경우 JMM은 최소한의 보안만 제공합니다. 스레드가 실행될 때 읽혀지는 값은 이전 각 스레드가 쓴 값은 기본값(0, null, false)입니다. JMM은 스레드 읽기 작업에서 읽은 값이 갑자기 나타나지 않도록 합니다. 최소한의 안전성을 달성하기 위해 JVM은 힙에 객체를 할당할 때 먼저 메모리 공간을 지운 다음 그 위에 객체를 할당합니다(JVM은 이 두 작업을 내부적으로 동기화합니다). 따라서 객체가 미리 0으로 지정된 메모리로 할당되면 도메인의 기본 초기화가 이미 완료됩니다. JMM은 비동기화된 프로그램의 실행 결과가 순차 일관성 모델의 프로그램 실행 결과와 일치함을 보장하지 않습니다. 비동기화 프로그램이 순차 일관성 모델에서 실행되면 일반적으로 순서가 어긋나고 실행 결과를 예측할 수 없기 때문입니다. 동기화되지 않은 프로그램이 두 모델 모두에서 일관된 결과로 실행되도록 보장하는 것은 의미가 없습니다. 순차적 일관성 모델과 유사하게, 비동기화된 프로그램이 JMM에서 실행되면 일반적으로 순서가 어긋나고 실행 결과를 예측할 수 없습니다. 동시에, 이 두 모델의 비동기화 프로그램의 실행 특성에는 다음과 같은 차이점이 있습니다. 순차 일관성 모델은 단일 스레드 내의 작업이 프로그램 순서대로 실행된다는 것을 보장하는 반면, JMM은 이를 보장하지 않습니다. 단일 스레드 내의 작업은 프로그램 순서대로 실행됩니다(예: 임계 섹션 내에서 올바르게 동기화된 다중 스레드 프로그램의 위 순서 변경). 이 내용은 이전에 논의되었으므로 여기서는 반복하지 않겠습니다. 순차적 일관성 모델은 모든 스레드가 일관된 작업 실행 순서만 볼 수 있음을 보장하는 반면, JMM은 모든 스레드가 일관된 작업 실행 순서를 볼 수 있음을 보장하지 않습니다. 이는 이전에 언급되었으므로 여기서는 반복하지 않겠습니다. JMM은 64비트 long 및 double 변수에 대한 읽기/쓰기 작업에 대한 원자성을 보장하지 않는 반면, 순차 일관성 모델은 모든 메모리 읽기/쓰기 작업에 대한 원자성을 보장합니다. 세 번째 차이점은 프로세서 버스의 작동 메커니즘과 밀접한 관련이 있습니다. 컴퓨터에서는 버스를 통해 프로세서와 메모리 사이에 데이터가 전달됩니다. 프로세서와 메모리 간의 각 데이터 전송은 버스 트랜잭션이라고 하는 일련의 단계를 통해 완료됩니다. 버스 트랜잭션에는 읽기 트랜잭션과 쓰기 트랜잭션이 포함됩니다. 읽기 트랜잭션은 메모리에서 프로세서로 데이터를 전송하고, 쓰기 트랜잭션은 프로세서에서 메모리로 데이터를 전송합니다. 각 트랜잭션은 메모리에서 물리적으로 연속된 하나 이상의 단어를 읽고 씁니다. 여기서 중요한 점은 버스가 버스를 동시에 사용하려고 시도하는 트랜잭션을 동기화한다는 것입니다. 한 프로세서가 버스 트랜잭션을 실행하는 동안 버스는 다른 모든 프로세서와 I/O 장치가 메모리를 읽고 쓰는 것을 금지합니다. 회로도를 통해 버스의 작동 메커니즘을 설명하겠습니다. 위 그림에서 볼 수 있듯이 프로세서 A, B, C가 동시에 버스에 대한 버스 트랜잭션을 시작한다고 가정합니다. 이때 버스 중재가 경쟁을 결정한다고 가정합니다. 프로세서 A는 중재 후 경쟁합니다. 버스 중재는 모든 프로세서가 메모리에 공정하게 액세스할 수 있도록 보장합니다. 이때 프로세서 A는 버스 트랜잭션을 계속하는 반면, 다른 두 프로세서는 메모리 액세스를 다시 수행하기 전에 프로세서 A의 버스 트랜잭션이 완료될 때까지 기다려야 합니다. 프로세서 A가 버스 트랜잭션을 실행하는 동안(버스 트랜잭션이 읽기 트랜잭션인지 쓰기 트랜잭션인지에 관계없이) 프로세서 D가 버스에 대한 버스 트랜잭션을 시작한다고 가정합니다. 이때 프로세서 D의 요청은 버스에 의해 금지됩니다. . 이러한 버스 작동 메커니즘은 언제든지 직렬 방식으로 모든 프로세서의 메모리 액세스를 수행할 수 있으며 최대 하나의 프로세서만 메모리에 액세스할 수 있습니다. 이 기능은 단일 버스 트랜잭션 내의 메모리 읽기/쓰기 작업이 원자성임을 보장합니다. 일부 32비트 프로세서에서 64비트 데이터의 읽기/쓰기 작업이 원자성이어야 하는 경우 상대적으로 큰 오버헤드가 발생합니다. 이러한 종류의 프로세서를 처리하기 위해 Java 언어 사양에서는 JVM이 64비트 긴 변수 및 이중 변수를 읽고 쓰는 데 원자성을 갖도록 권장하지만 요구하지는 않습니다. JVM이 이러한 프로세서에서 실행되면 실행을 위해 64비트 long/double 변수의 읽기/쓰기 작업을 두 개의 32비트 읽기/쓰기 작업으로 분할합니다. 이 두 가지 32비트 읽기/쓰기 작업은 실행을 위해 서로 다른 버스 트랜잭션에 할당될 수 있습니다. 이때 이 64비트 변수의 읽기/쓰기는 원자적이지 않습니다. 단일 메모리 작업이 원자적이지 않으면 예상치 못한 결과가 발생할 수 있습니다. 아래 다이어그램을 보세요. 위 그림과 같이 프로세서 A가 Long 변수를 쓰고 프로세서 B가 이 Long 변수를 읽으려고 한다고 가정합니다. 프로세서 A의 64비트 쓰기 작업은 두 개의 32비트 쓰기 작업으로 분할되고 두 개의 32비트 쓰기 작업은 실행을 위해 서로 다른 쓰기 트랜잭션에 할당됩니다. 동시에 프로세서 B의 64비트 읽기 작업은 두 개의 32비트 읽기 작업으로 분할되고 두 개의 32비트 읽기 작업은 실행을 위해 동일한 읽기 트랜잭션에 할당됩니다. 프로세서 A와 B가 위 그림의 타이밍 순서에 따라 실행되면 프로세서 B는 프로세서 A가 "절반만 쓴" 잘못된 값을 보게 됩니다. 위 내용은 Java 메모리 모델에 대한 심층 분석입니다. 순차 일관성에 대한 내용은 PHP 중국어 웹사이트(www.php.cn)를 참고하세요!