synchronized
키워드 의 사용법과 중요성에 대해 몇 가지 질문이 있습니다 .
synchronized
키워드 의 의미는 무엇입니까 ?- 방법은 언제해야
synchronized
합니까? - 프로그래밍 방식 및 논리적 의미는 무엇입니까?
synchronized
키워드 의 사용법과 중요성에 대해 몇 가지 질문이 있습니다 .
synchronized
키워드 의 의미는 무엇입니까 ?synchronized
합니까?답변:
synchronized
키워드는 읽기와 같은 변수, 객체, 자원에 쓰는 다른 스레드에 대한 모든 것입니다. 이것은 Java에서 사소한 주제는 아니지만 Sun의 인용문입니다.
synchronized
메소드는 스레드 간섭 및 메모리 일관성 오류를 방지하기위한 간단한 전략을 가능하게합니다. 오브젝트가 둘 이상의 스레드에 표시되면 해당 오브젝트 변수에 대한 모든 읽기 또는 쓰기는 동기화 된 메소드를 통해 수행됩니다.
매우 작은 요컨대 , 같은 '자원'에 읽고 쓰는 두 개의 스레드가있는 경우 (예 : 변수) foo
, 이러한 스레드가 원자 방식으로 변수에 액세스해야합니다. synchronized
키워드가 없으면 스레드 1이 변경 스레드 2를 보지 못 foo
하거나 더 이상 절반 만 변경 될 수 있습니다. 이것은 논리적으로 기대하는 것이 아닙니다.
다시 말하지만, 이것은 Java에서 사소한 주제입니다. 자세한 내용을 보려면 SO 및 인터 웹에 대한 다음 주제를 탐색하십시오.
"Brian Goetz" 라는 이름 이 뇌의 "동시성" 이라는 용어와 영구적으로 연관 될 때까지 이러한 주제를 계속 탐색하십시오 .
글쎄, 우리는 충분한 이론적 설명을 가지고 있다고 생각 하므로이 코드를 고려하십시오.
public class SOP {
public static void print(String s) {
System.out.println(s+"\n");
}
}
public class TestThread extends Thread {
String name;
TheDemo theDemo;
public TestThread(String name,TheDemo theDemo) {
this.theDemo = theDemo;
this.name = name;
start();
}
@Override
public void run() {
theDemo.test(name);
}
}
public class TheDemo {
public synchronized void test(String name) {
for(int i=0;i<10;i++) {
SOP.print(name + " :: "+i);
try{
Thread.sleep(500);
} catch (Exception e) {
SOP.print(e.getMessage());
}
}
}
public static void main(String[] args) {
TheDemo theDemo = new TheDemo();
new TestThread("THREAD 1",theDemo);
new TestThread("THREAD 2",theDemo);
new TestThread("THREAD 3",theDemo);
}
}
참고 : synchronized
이전 스레드의 실행이 완료되지 않는 한 다음 스레드의 메소드 test () 호출을 차단합니다. 스레드는 한 번에 하나씩이 메소드에 액세스 할 수 있습니다. synchronized
모든 스레드가 없으면 이 메소드에 동시에 액세스 할 수 있습니다.
스레드가 오브젝트의 동기화 된 메소드 'test'(여기서 오브젝트는 'TheDemo'클래스의 인스턴스 임)를 호출 할 때 해당 오브젝트의 잠금을 획득하면 새 스레드는 이전 스레드와 동일한 오브젝트의 동기화 된 메소드를 호출 할 수 없습니다 잠금을 획득 한 잠금은 해제되지 않습니다.
클래스의 정적 동기화 메소드가 호출 될 때도 비슷한 일이 발생합니다. 스레드는 클래스와 연관된 잠금을 획득합니다 (이 경우 해당 클래스 인스턴스의 정적이 아닌 동기화 된 메소드는 해당 오브젝트 레벨 잠금이 여전히 사용 가능하므로 스레드에 의해 호출 될 수 있음). 클래스 레벨 잠금이 현재 잠금을 보유한 스레드에 의해 해제되지 않는 한 다른 스레드는 클래스의 정적 동기화 메소드를 호출 할 수 없습니다.
동기화 된 출력
THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9
동기화되지 않은 출력
THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
synchronized
주지만 메모리 일관성은 무시됩니다.
synchronized
다중 스레드 환경에서, synchronized
메소드 / 블록을 갖는 객체 는 두 스레드가 synchronized
동시에 코드 의 메소드 / 블록 에 액세스하는 것을 허용하지 않습니다 . 즉, 한 스레드는 읽을 수없고 다른 스레드는이를 업데이트 할 수 없습니다.
두 번째 스레드는 대신 첫 번째 스레드가 실행을 완료 할 때까지 기다립니다. 오버 헤드는 속도이지만 이점은 데이터의 일관성을 보장합니다.
응용 프로그램이 단일 스레드 인 경우 synchronized
블록은 이점을 제공하지 않습니다.
synchronized
키워드 방식을 입력 할 때 (이 정적 메소드가 아니면, 지정된 객체 인스턴스에 대한) 단 하나 개의 스레드가 동시에 상기 방법을 실행할 수 있도록하는 것이, 로크를 획득하기 위해 스레드를 야기한다.
이것을 클래스 안전 스레드 만들기라고 부르지 만 이것이 완곡주의라고 말할 것입니다. 동기화가 Vector의 내부 상태가 손상되는 것을 방지하는 것은 사실이지만 일반적으로 Vector 사용자에게 큰 도움이되지는 않습니다.
이걸 고려하세요:
if (vector.isEmpty()){
vector.add(data);
}
관련된 메소드는 동기화되어 있지만 개별적으로 잠금 및 잠금 해제되어 있기 때문에 불행하게도 시간이 걸리는 두 개의 스레드가 두 개의 요소로 벡터를 만들 수 있습니다.
따라서 실제로 응용 프로그램 코드에서도 동기화해야합니다.
메소드 레벨 동기화는 a) 필요하지 않을 때 비싸고 b) 동기화가 필요할 때 불충분하기 때문에 동기화되지 않은 대체 (Vector의 경우 ArrayList)가 있습니다.
최근에는 멀티 스레딩 문제를 처리하는 많은 영리한 유틸리티와 함께 동시성 패키지가 릴리스되었습니다.
Java에서 동기화 된 키워드는 스레드 안전성과 관련이 있습니다. 즉, 여러 스레드가 동일한 변수를 읽거나 쓸 때입니다.
이것은 같은 변수에 직접 액세스하거나 같은 변수에 액세스하는 다른 클래스를 사용하는 클래스를 사용하여 간접적으로 발생할 수 있습니다.
synchronized 키워드는 여러 스레드가 동일한 변수에 안전하게 액세스 할 수있는 코드 블록을 정의하는 데 사용됩니다.
구문 측면에서 synchronized
키워드는 Object
매개 변수 ( lock 객체 라고 함)를 취한 다음 뒤에을 붙 { block of code }
입니다.
실행시이 키워드가 발생하면 현재 스레드는 잠금 개체 를 "잠금 / 취득 / 소유"(선택)하고 잠금 이 획득 된 후 관련 코드 블록을 실행 하려고합니다 .
동기화 된 코드 블록 내의 변수에 대한 모든 쓰기는 동일한 잠금 객체를 사용하여 동기화 된 코드 블록 내의 코드를 유사하게 실행하는 다른 모든 스레드에서 볼 수 있도록 보장됩니다 .
한 번에 하나의 스레드 만 잠금을 보유 할 수 있으며이 시간 동안 동일한 잠금 오브젝트 를 확보하려는 다른 모든 스레드 는 대기합니다 (실행 일시 중지). 실행이 동기화 된 코드 블록을 종료하면 잠금이 해제됩니다.
추가 synchronized
메소드 정의하면 키워드와 동기 코드 블록에서 전체 랩핑 방법 본체 같다 로크 대상의 존재 this
(예 방법의 경우) 및 ClassInQuestion.getClass()
(클래스 메소드) .
-인스턴스 메소드는 static
키워드 가없는 메소드입니다 .
-클래스 메소드는 static
키워드 가있는 메소드입니다 .
동기화가 없으면 읽기 및 쓰기 순서가 보장되지 않아 변수에 가비지가 남을 수 있습니다.
예를 들어 변수는 한 스레드가 쓴 비트의 절반과 다른 스레드가 쓴 비트의 절반으로 끝나서 스레드 중 어느 것도 쓰지 않았지만 두 가지가 혼잡 한 상태로 변수를 남길 수 있습니다.
하드웨어가 변수의 값을 캐시 할 수 있고 읽기 스레드가 작성된 값 대신 캐시 된 값을 볼 수 있기 때문에 다른 스레드가 읽기 전에 (벽 시계 시간) 스레드에서 쓰기 조작을 완료하는 것만으로는 충분하지 않습니다. 그것.
따라서 Java의 경우 스레딩 오류가 발생하지 않도록 Java 메모리 모델을 따라야합니다.
다시 말해 : 후드 아래에서 동기화, 원 자성 작업 또는 클래스를 사용하는 클래스를 사용하십시오.
출처
http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Java® 언어 사양, 2015-02-13
동기화 된 키워드 란 무엇입니까?
스레드는 주로 필드에 대한 액세스를 공유하여 통신하며 개체 참조 필드가 참조합니다. 이 통신 방식은 매우 효율적이지만 스레드 간섭 및 메모리 일관성 오류 라는 두 가지 유형의 오류가 발생할 수 있습니다 . 이러한 오류를 방지하는 데 필요한 도구는 동기화입니다.
동기화 된 블록 또는 방법은 스레드 간섭을 방지하고 데이터의 일관성을 유지합니다. 어느 시점에서든 하나의 스레드 만 잠금을 획득하여 동기화 된 블록 또는 방법 ( 중요 섹션 )에 액세스 할 수 있습니다 . 다른 스레드는 중요한 섹션 에 액세스하기 위해 잠금 해제를 기다립니다 .
메소드는 언제 동기화됩니까?
메소드 synchronized
정의 또는 선언에 추가하면 메소드가 동기화됩니다 . 메소드 내에서 특정 코드 블록을 동기화 할 수도 있습니다.
문법적으로나 논리적으로 무슨 뜻입니까?
잠금을 획득하여 하나의 스레드 만 중요한 섹션 에 액세스 할 수 있음을 의미합니다 . 이 스레드가이 잠금을 해제하지 않으면 다른 모든 스레드는 잠금을 획득하기 위해 대기해야합니다. 그들은 잠금을 획득 하지 않고 임계 구역 에 들어갈 수 없습니다 .
이것은 마술로 할 수 없습니다. 응용 프로그램에서 중요한 섹션 을 식별 하고 적절히 보호하는 것은 프로그래머의 책임 입니다. Java는 애플리케이션을 보호하기위한 프레임 워크를 제공하지만 보호해야 할 모든 섹션과 위치는 프로그래머의 책임입니다.
Java 문서 페이지 에서 자세한 내용
본질 잠금 및 동기화 :
동기화는 내장 잠금 또는 모니터 잠금이라고하는 내부 엔터티를 기반으로합니다. 내장 잠금은 동기화의 두 측면에서 중요한 역할을합니다. 즉 객체 상태에 대한 독점 액세스를 강화하고 가시성에 필수적인 선행 관계를 설정합니다.
모든 객체에는 관련된 고유 잠금이 있습니다 . 일반적으로 객체 필드에 독점적이고 일관된 액세스가 필요한 스레드는 객체에 액세스하기 전에 객체의 본질적 잠금을 획득 한 다음 완료되면 본질적 잠금을 해제해야합니다.
스레드는 잠금을 획득하고 잠금을 해제 한 시간 사이에 고유 잠금을 소유한다고합니다. 스레드가 내장 잠금을 소유하는 한 다른 스레드는 동일한 잠금을 얻을 수 없습니다. 다른 스레드는 잠금을 획득하려고 시도 할 때 차단됩니다.
스레드가 내장 잠금을 해제하면 해당 조치와 동일한 잠금의 후속 획득간에 발생 전 관계가 설정됩니다.
메소드를 동기화하는 데는 두 가지 효과가 있습니다 .
첫째, 동일한 객체에서 동기화 된 메소드를 두 번 호출하여 인터리브 할 수 없습니다.
하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).
둘째, 동기화 된 메소드가 종료되면 동일한 오브젝트에 대한 동기화 된 메소드의 후속 호출과의 사전 관계를 자동으로 설정합니다.
이렇게하면 객체 상태에 대한 변경 사항이 모든 스레드에 표시됩니다.
다음에서 동기화에 대한 다른 대안을 찾으십시오.
Synchronized normal method
상응하는
Synchronized statement
(이 사용)
class A {
public synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(this) {
// all function code
}
}
}
Synchronized static method
Synchronized statement
(사용 클래스) 와 동등
class A {
public static synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(A.class) {
// all function code
}
}
}
동기화 된 명령문 (변수 사용)
class A {
private Object lock1 = new Object();
public void methodA() {
synchronized(lock1 ) {
// all function code
}
}
}
를 들어 synchronized
, 우리는 모두 가지고 Synchronized Methods
와 Synchronized Statements
. 그러나 Synchronized Methods
와 비슷 Synchronized Statements
하므로 이해해야 Synchronized Statements
합니다.
=> 기본적으로 우리는
synchronized(object or class) { // object/class use to provides the intrinsic lock
// code
}
이해하는 데 도움이되는 2 가지 생각이 있습니다. synchronized
intrinsic lock
관련되어 있습니다.synchronized statement
대한를 자동으로 획득 하여 메소드가 반환 될 때 해제합니다. 한 스레드가 소유으로 , 다른 스레드는 얻을 수 없다 SAME 잠금 => 스레드 금고.intrinsic lock
synchronized statement's
intrinsic lock
=> thread A
호출 할 때 synchronized(this){// code 1}
=> SAME 잠금으로 인해 synchronized(this)
모든 블록 코드 (클래스 내부) 와 모든 synchronized normal method
(클래스 내부)이 잠 깁니다. 잠금 해제 후 실행됩니다 ( "// code 1"완료). thread A
이 동작은 유사하다 synchronized(a variable){// code 1}
나 synchronized(class)
.
SAME LOCK => lock (어떤 방법에 의존하지 않습니까?
synchronized statements
더 확장 가능하기 때문에 선호합니다 . 예를 들어, 나중에 메소드의 일부만 동기화하면됩니다. 예, 당신은이 방법을 동기화하고는 상관없는 그것 (그 사용에 의해 방지 할 수있는 다른 방법을 차단합니다, 스레드가 메소드를 실행하지만 때, 서로 관련을 synchronized(a variable)
).
그러나 동기화 방법을 적용하는 것은 간단하고 코드는 단순 해 보입니다. 일부 클래스의 경우 하나의 동기화 된 메소드 또는 클래스와 관련된 모든 동기화 된 메소드 만 있습니다 => synchronized method
코드를 더 짧고 이해하기 쉽게 만드는 데 사용할 수 있습니다
(와 관련이 많지 않으므로 synchronized
객체와 클래스가 다르거 나 정적 및 정적이 아닙니다).
synchronized
또는 정상적인 방법이나 synchronized(this)
또는 synchronized(non-static variable)
각 객체 인스턴스에 기반을 동기화합니다. synchronized
또는 정적 방법 또는 synchronized(class)
또는 synchronized(static variable)
이 클래스에 기반을 동기화합니다https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
도움이되기를 바랍니다.
다음은 The Java Tutorials의 설명 입니다.
다음 코드를 고려하십시오.
public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
count
의 인스턴스 인 경우SynchronizedCounter
이러한 메소드를 동기화하면 두 가지 효과가 있습니다.
- 첫째, 동일한 객체에서 동기화 된 메소드를 두 번 호출하여 인터리브 할 수 없습니다. 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).
- 둘째, 동기화 된 메소드가 종료되면 동일한 오브젝트에 대한 동기화 된 메소드의 후속 호출과의 사전 관계를 자동으로 설정합니다. 이렇게하면 객체 상태에 대한 변경 사항이 모든 스레드에 표시됩니다.
다른 답변에서 누락 된 것은 메모리 장벽이라는 중요한 측면 중 하나 입니다. 스레드 동기화는 기본적으로 직렬화와 가시성 이라는 두 부분 으로 구성됩니다 . 나는 "jvm memory barrier"에 대해 구글에게 조언을 해줄 것을 권한다. 이는 사소하고 매우 중요한 주제이기 때문이다 (여러 스레드가 액세스하는 공유 데이터를 수정하는 경우). 그렇게 한 후에는 명시 적 동기화 사용을 피하는 데 도움이되는 java.util.concurrent 패키지의 클래스를 살펴 보는 것이 좋습니다.이 클래스는 프로그램을 간단하고 효율적으로 유지하고 교착 상태를 방지 할 수 있습니다.
그러한 예 중 하나는 ConcurrentLinkedDeque 입니다. 명령 패턴 과 함께 명령을 동시 큐에 채워서 매우 효율적인 작업자 스레드를 만들 수 있습니다. 명시적인 동기화가 필요없고 교착 상태가없고 명시적인 sleep ()이 필요하지 않으며 take ()를 호출하여 큐를 폴링하면됩니다.
한마디로 : "메모리 동기화" 는 스레드를 시작하고 스레드가 종료 될 때 휘발성 변수를 읽고 모니터 잠금을 해제합니다 (동기화 된 블록 / 함수를 떠남) 등 "암호화"가 암시 적으로 발생합니다. ") 모든 작업은 해당 특정 작업 전에 수행됩니다. 위에서 언급 한 ConcurrentLinkedDeque의 경우, 문서는 "말한다":
메모리 일관성 효과 : 다른 동시 콜렉션과 마찬가지로, 오브젝트를 ConcurrentLinkedDeque에 배치하기 전에 스레드의 조치는 다른 스레드의 ConcurrentLinkedDeque에서 해당 요소를 액세스하거나 제거한 후에 발생 하는 조치 이전에 발생 합니다.
경험이없는 대부분의 Java 프로그래머는 그로 인해 주어진 시간이 많이 걸리기 때문에 이러한 암묵적인 동작은 다소 위험한 측면입니다. 그런 다음 Java가 다른 작업로드가있는 프로덕션에서 수행하는 "추정 된"작업을 수행하지 않은 후이 스레드를 갑자기 우연히 발견하고 동시성 문제를 테스트하기가 매우 어렵습니다.
동기화는 단순히 단일 객체와 연결된 경우 여러 스레드가 동기화 된 블록이 특정 객체에 사용되는 경우 더티 읽기 및 쓰기를 방지 할 수 있음을 의미합니다. 더 명확하게하기 위해 예를 들어 보겠습니다.
class MyRunnable implements Runnable {
int var = 10;
@Override
public void run() {
call();
}
public void call() {
synchronized (this) {
for (int i = 0; i < 4; i++) {
var++;
System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
}
}
}
}
public class MutlipleThreadsRunnable {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread t1 = new Thread(runnable1);
t1.setName("Thread -1");
Thread t2 = new Thread(runnable2);
t2.setName("Thread -2");
Thread t3 = new Thread(runnable1);
t3.setName("Thread -3");
t1.start();
t2.start();
t3.start();
}
}
우리는 2 개의 MyRunnable 클래스 객체를 만들었습니다. runnable1은 스레드 1과 공유되고 스레드 3과 runnable2는 스레드 2와 공유됩니다. 이제 t1과 t3이 동기화되지 않고 시작될 때 PFB 출력은 스레드 1과 3 모두 스레드 2에 대한 var 값에 동시에 영향을 미치며 var는 자체 메모리를 가지고 있음을 나타냅니다.
Without Synchronized keyword
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -2 var value 12
Current Thread Thread -2 var value 13
Current Thread Thread -2 var value 14
Current Thread Thread -1 var value 12
Current Thread Thread -3 var value 13
Current Thread Thread -3 var value 15
Current Thread Thread -1 var value 14
Current Thread Thread -1 var value 17
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 18
스레드 3은 동기화를 사용하여 모든 시나리오에서 스레드 1이 완료 될 때까지 대기합니다. 스레드 1과 스레드 3이 공유하는 runnable1과 스레드 2 만 공유하는 runnable2에 두 개의 잠금이 있습니다.
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
동기화 된 단순은 두 스레드가 동시에 블록 / 방법에 액세스 할 수 없음을 의미합니다. 클래스의 블록 / 메소드가 동기화되었다고하면 한 번에 하나의 스레드 만 액세스 할 수 있습니다. 내부적으로 액세스를 시도하는 스레드는 먼저 해당 객체를 잠그고이 잠금을 사용할 수없는 한 다른 스레드는 해당 클래스 인스턴스의 동기화 된 메서드 / 블록에 액세스 할 수 없습니다.
다른 스레드는 동기화되도록 정의되지 않은 동일한 객체의 메서드에 액세스 할 수 있습니다. 스레드는 다음을 호출하여 잠금을 해제 할 수 있습니다.
Object.wait()
synchronized
Java의 블록은 멀티 스레딩의 모니터입니다. synchronized
동일한 객체 / 클래스를 가진 블록은 단일 스레드로만 실행될 수 있으며 다른 모든 스레드는 기다리고 있습니다. race condition
여러 스레드가 동일한 변수를 업데이트하려고 할 때 상황을 도울 수 있습니다 (첫 번째 단계는 volatile
정보 사용 )
Java 5
[정보]synchronized
를 지원하여 확장happens-before
모니터의 잠금 해제 (동기화 블록 또는 메소드 종료)는 동일한 모니터의 모든 후속 잠금 (동기화 블록 또는 메소드 입력) 이전에 발생합니다.
다음 단계는 java.util.concurrent