1. 동기화 된 기본 사용
동기화 된 것은 Java에서 동시성 문제를 해결하는 데 가장 일반적으로 사용되는 방법과 가장 쉬운 방법입니다. Synchronized는 세 가지 주요 기능을 가지고 있습니다. (1) 스레드 상호 독점 액세스 동기화 코드 (2) 공유 변수의 수정이 적시에 가시 될 수 있는지 확인하십시오 (3) 재주문 문제를 효과적으로 해결하십시오. Synchronized는 세 가지 동기화를 사용합니다.
(1) 일반적인 수정 방법
(2) 정적 방법을 수정합니다
(3) 코드 블록을 수정하십시오
다음으로, 나는이 세 가지 사용법을 설명하기 위해 몇 가지 예제 프로그램을 사용합니다 (동기화 된 다른 사용 방법을 제외하고는 다른 세 코드는 기본적으로 일관성이 있습니다).
1. 동기화 없음 :
코드 스 니펫 1 :
package com.paddx.test.concurrent; public class synchronizedtest {public void method1 () {system.out.println ( "method 1 start"); {system.out.println ( "메소드 1 실행"); Thread.sleep (3000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 1 끝"); } public void method2 () {system.out.println ( "메소드 2 시작"); try {System.out.println ( "methys 2 execute"); Thread.sleep (1000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 2 끝"); } public static void main (String [] args) {Final SynchronizedTest test = new SynchronizedTest (); 새 스레드 (new Runnable () {@override public void run () {test.method1 ();}}). start (); 새 스레드 (new Runnable () {@override public void run () {test.method2 ();}}). start (); }}실행 결과는 다음과 같습니다. 스레드 1 및 스레드 2는 동시에 실행 상태를 입력합니다. 스레드 2는 스레드 1보다 빠르게 실행되므로 스레드 2가 먼저 실행됩니다. 이 과정에서 스레드 1 및 스레드 2는 동시에 실행됩니다.
방법 1 시작
방법 1 실행
방법 2 시작
방법 2 실행
방법 2 끝
방법 1 끝
2. 공통 방법 동기화 :
코드 스 니펫 2 :
package com.paddx.test.concurrent; public class synchronizedtest {public synchronized void method1 () {system.out.println ( "methys 1 start"); {system.out.println ( "메소드 1 실행"); Thread.sleep (3000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 1 끝"); } public synchronized void method2 () {system.out.println ( "methys 2 start"); try {System.out.println ( "methys 2 execute"); Thread.sleep (1000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 2 끝"); } public static void main (String [] args) {Final SynchronizedTest test = new SynchronizedTest (); 새 스레드 (new Runnable () {@override public void run () {test.method1 ();}}). start (); 새 스레드 (new Runnable () {@override public void run () {test.method2 ();}}). start (); }}실행 결과는 다음과 같습니다. 코드 세그먼트와 비교 한 후, Method2 메소드를 실행하기 시작하기 전에 스레드 2가 전체 스레드 1의 실행을 기다려야한다는 것을 분명히 알 수 있습니다.
방법 1 시작
방법 1 실행
방법 1 끝
방법 2 시작
방법 2 실행
방법 2 끝
3. 정적 메소드 (클래스) 동기화
코드 스 니펫 3 :
패키지 com.paddx.test.concurrent; public class synchronizedtest {public static synchronized void method1 () {system.out.println ( "method 1 start"); {system.out.println ( "메소드 1 실행"); Thread.sleep (3000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 1 끝"); } public static synchronized void method2 () {system.out.println ( "methys 2 start"); try {System.out.println ( "methys 2 execute"); Thread.sleep (1000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 2 끝"); } public static void main (String [] args) {Final SynchronizedTest test = new SynchronizedTest (); Final SynchronizedTest Test2 = New SynchronizedTest (); 새 스레드 (new Runnable () {@override public void run () {test.method1 ();}}). start (); 새 스레드 (new Runnable () {@override public void run () {test2.method2 ();}}). start (); }}실행 결과는 다음과 같습니다. 정적 메소드의 동기화는 본질적으로 클래스의 동기화입니다 (정적 메소드는 객체의 메소드가 아니라 본질적으로 클래스의 방법입니다). 따라서 Test와 Test2가 다른 객체에 속해 있어도 동기화 테스트 클래스의 인스턴스에 속하므로 Method1 및 Method2를 순차적으로 만 실행할 수 있으며 동시에 실행할 수 없습니다.
방법 1 시작
방법 1 실행
방법 1 끝
방법 2 시작
방법 2 실행
방법 2 끝
4. 코드 블록 동기화
코드 스 니펫 4 :
package com.paddx.test.concurrent; public class synchronizedtest {public void method1 () {system.out.println ( "method 1 start"); try {synchronized (this) {system.out.println ( "method 1 execute"); Thread.sleep (3000); }} catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 1 끝"); } public void method2 () {system.out.println ( "메소드 2 시작"); try {synchronized (this) {system.out.println ( "methys 2 execute"); Thread.sleep (1000); }} catch (InterruptedException e) {e.printstacktrace (); } system.out.println ( "메소드 2 끝"); } public static void main (String [] args) {Final SynchronizedTest test = new SynchronizedTest (); 새 스레드 (new Runnable () {@override public void run () {test.method1 ();}}). start (); 새 스레드 (new Runnable () {@override public void run () {test.method2 ();}}). start (); }}실행 결과는 다음과 같습니다. 스레드 1과 스레드 2가 모두 해당 메소드를 입력하고 실행을 시작하지만 스레드 2는 동기화 블록을 입력하기 전에 스레드 1의 동기화 블록 실행이 완료 될 때까지 기다려야합니다.
방법 1 시작
방법 1 실행
방법 2 시작
방법 1 끝
방법 2 실행
방법 2 끝
2. 동기화 된 원리
위의 실행 결과에 대해 여전히 궁금한 점이 있다면 걱정하지 마십시오. 먼저 동기화 된 원리를 이해 한 다음 위의 질문을 되돌아보고 한 눈에 보자. 먼저 동기화 된 코드가 다음 코드를 디 컴파일하여 코드 블록을 동기화하는 방법을 살펴 보겠습니다.
package com.paddx.test.concurrent; public class synchronizeddemo {public void method () {synchronized (this) {system.out.println ( "method 1 start"); }}}계산 결과 :
이 두 지침의 역할과 관련하여 JVM 사양의 설명을 직접 참조합니다.
MonitorEnter :
각 객체는 모니터와 관련이 있습니다. 소유자가있는 경우에만 모니터가 잠겨 있습니다. MonitorEnter를 실행하는 스레드는 다음과 같이 OBJectREF와 관련된 모니터의 소유권을 얻으려고 시도합니다. • OBJEctREF와 관련된 모니터의 입력 수가 0이면 스레드가 모니터로 들어가 입력 수를 하나로 설정합니다. 스레드는 모니터의 소유자입니다. • 스레드가 이미 ObjectRef와 관련된 모니터를 소유하고 모니터를 다시 입력하여 입력 카운트를 증가시킵니다. • 다른 스레드가 이미 OBJectREF와 관련된 모니터를 소유하고 있으면 모니터의 입력 수가 0이 될 때까지 스레드가 다시 소유권을 얻으려고 시도합니다.
이 구절의 일반적인 의미는 다음과 같습니다.
각 객체에는 모니터 잠금 (모니터)이 있습니다. 모니터가 점유되면 잠겨 있습니다. 스레드가 Moniterenter 명령을 실행하면 모니터의 소유권을 얻으려고합니다. 프로세스는 다음과 같습니다.
1. 모니터의 항목 수가 0이면 스레드가 모니터로 들어간 다음 입력 번호를 1로 설정하면 스레드는 모니터의 소유자입니다.
2. 스레드가 이미 모니터를 소유하고 다시 입력하면 모니터에 들어가는 수가 1에 추가됩니다.
3. 다른 스레드가 모니터를 점유 한 경우, 모니터의 입력 횟수가 0이 될 때까지 스레드가 차단 상태로 들어간 다음 모니터의 소유권을 다시 얻으려고 노력합니다.
Monitorexit :
MonitoreXit을 실행하는 스레드는 OBJEctREF가 참조한 인스턴스와 관련된 모니터의 소유자 여야합니다. 스레드는 OBJEctREF와 관련된 모니터의 입력 수를 줄입니다. 결과적으로 입력 카운트의 값이 0 인 경우 스레드는 모니터를 종료하고 더 이상 소유자가 아닙니다. 모니터에 입력하기 위해 차단되는 다른 스레드를 시도합니다.
이 구절의 일반적인 의미는 다음과 같습니다.
MonitoreXit을 실행하는 스레드는 OBJEctREF에 해당하는 모니터의 소유자 여야합니다.
명령어가 실행되면 입력 모니터 수는 1만큼 줄어 듭니다. 1만큼 감소한 후 입력 한 모니터 수가 0이면 스레드가 모니터를 종료하고 더 이상이 모니터의 소유자가 아닙니다. 이 모니터로 차단 된 다른 스레드는이 모니터의 소유권을 얻을 수 있습니다.
이 두 가지 설명 단락을 통해 동기화 된 구현 원리를 명확하게 볼 수 있어야합니다. 동기화 된 시맨틱 기본 층은 모니터 객체를 통해 완료됩니다. 실제로 대기/알림 및 기타 방법은 모니터 객체에 의존합니다. 그렇기 때문에 대기/알림과 같은 메소드 만 동기화 된 블록 또는 메소드에서 호출 할 수있는 이유입니다.
동기화 방법의 소환 결과를 살펴 보겠습니다.
소스 코드 :
package com.paddx.test.concurrent; public class synchronizedmethod {public synchronized void method () {system.out.println ( "Hello World!"); }}계산 결과 :
디 컴파일 결과를 판단하면, 메소드의 동기화는 Monitorenter 및 MonitoreXit 지침을 통해 완료되지 않습니다 (이론적으로는이 두 지침을 통해 구현 될 수도 있음). 그러나 일반적인 방법과 비교하여 ACC_SynChronized 식별자가 일정한 풀에 추가됩니다. JVM 은이 식별자를 기반으로 메소드의 동기화를 구현합니다. 메소드가 호출되면 호출 명령어는 메소드의 ACC_SynChronized 액세스 플래그가 설정되어 있는지 확인합니다. 설정된 경우 실행 스레드가 먼저 모니터를 얻은 다음 메소드가 성공적으로 실행 된 후 메소드 본문을 실행합니다. 방법이 실행되면 모니터가 해제됩니다. 메소드 실행 중에 다른 스레드는 더 이상 동일한 모니터 객체를 얻을 수 없습니다. 실제로 본질적으로 차이는 없지만, 방법의 동기화는 바이트 코드를 통해 수행 할 필요없이이를 달성하는 암시적인 방법입니다.
3. 운영 결과에 대한 설명
동기화의 원리를 이해하면 위의 프로그램을 살펴보면 쉽게 해결할 수 있습니다.
1. 코드 세그먼트 2 결과 :
Method1과 Method2는 다른 방법이지만 두 방법 모두 동기화되고 동일한 객체를 통해 호출됩니다. 따라서 호출하기 전에 동일한 객체에서 잠금 (모니터)을 놓고 경쟁해야하므로 상호 독점적으로 잠금을 얻을 수 있습니다. 따라서, 방법 1 및 방법 2는 순차적으로 만 실행될 수있다.
2. 코드 세그먼트 3 결과 :
Test2와 Test2는 다른 객체에 속하지만 Test 및 Test2는 동일한 클래스의 다른 인스턴스에 속합니다. Method1과 Method2는 모두 정적 동기화 방법에 속하므로 동일한 클래스에서 모니터를 가져와야하므로 (각 클래스는 하나의 클래스 객체에만 해당) 순차적으로 만 실행할 수 있습니다.
3. 코드 세그먼트 4 결과 :
코드 블록의 동기화를 위해서는 동기화 된 키워드 후 괄호 안의 객체 모니터를 얻는 것이 본질적으로 필요합니다. 이 코드의 브래킷의 내용은 이것이며, Method1과 Method2는 동일한 객체를 통해 호출되므로 동기화 블록을 입력하기 전에 동일한 객체의 잠금에 대해 경쟁해야하므로 동기화 블록을 순서대로 만 실행할 수 있습니다.
네 가지 요약
동기화 된 것은 Java 동시 프로그래밍에서 스레드 안전에 가장 일반적으로 사용되는 방법이며 사용하기가 비교적 간단합니다. 그러나 원칙을 깊이 이해하고 모니터 잠금과 같은 기본 지식을 이해할 수 있다면 동기화 된 키워드를 올바르게 사용하는 데 도움이 될 수 있으며 반면에 동시성 프로그래밍 메커니즘을 더 잘 이해하고 다른 상황에서 작업을 완료하기위한 더 나은 동시성 전략을 선택하는 데 도움이 될 수 있습니다. 또한 일상 생활에서 발생하는 다양한 동시 문제를 침착하게 처리 할 수 있습니다.