이 장에서는 동기화 된 키워드를 소개합니다. 관련된 내용에는 다음이 포함됩니다.
1. 동기화 된 원리
2. 동기화 된 기본 규칙
3. 동기화 된 방법 및 동기화 된 코드 블록
4. 인스턴스 잠금 및 글로벌 잠금
1. 동기화 된 원리
Java에서 각 객체에는 하나의 동기화 잠금 만 가지고 있으며 있습니다. 이것은 또한 객체에 동기화 잠금이 존재 함을 의미합니다.
객체의 동기화 된 메소드를 호출하면 객체의 동기화 잠금을 얻습니다. 예를 들어, Synchronized (OBJ)는 "OBJ 객체"의 동기화 잠금을 획득합니다.
다른 스레드에 의한 동기화 잠금에 대한 액세스는 상호 배타적입니다. 다시 말해, 특정 시점에서 객체의 동기화 잠금은 하나의 스레드로만 얻을 수 있습니다! 동기화 잠금을 통해 여러 스레드에서 "객체/메소드"에 대한 상호 배타적 인 액세스를 얻을 수 있습니다. 예를 들어, 이제 두 개의 스레드 A와 스레드 B가 있으며,이 두 스레드 B는 "객체 OBJ의 동기 잠금"에 액세스합니다. 어느 시점에서 Thread A는 "OBJ의 동기화 잠금"을 획득하고 일부 작업을 수행한다고 가정합니다.이 시점에서 Thread B는 "OBJ의 동기화 잠금"을 얻으려고합니다. B는 스레드 A가 "이 객체의 동기 잠금 잠금"을 릴리스 할 때까지 "OBJ의 동기화 잠금"만 얻을 수 있으며 실행할 수 있습니다.
2. 동기화 된 기본 규칙
우리는 다음 3에 동기화 된 기본 규칙을 요약하고 예를 통해 설명합니다.
제 1 조 : 스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 "동기화 된 메소드"또는 "객체"의 "동기화 된 코드 블록"에 대한 액세스에서 차단됩니다.
제 2 조 : 스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스 할 때 다른 스레드는 여전히 "이 개체"의 비동기 코드 블록에 액세스 할 수 있습니다.
제 3 조 : 스레드가 "특정 객체"의 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 "객체"의 다른 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스 할 수 없습니다.
제 1 조
스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 "동기화 된 메소드"또는 "객체"의 "동기화 된 코드 블록"으로의 액세스에서 차단됩니다.
아래는 "동기화 된 코드 블록"에 해당하는 데모 프로그램입니다.
코드 사본은 다음과 같습니다.
클래스 신비로운 구현 런닝 가능한 {
@보수
public void run () {
동기화 (this) {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
공개 클래스 Demo1_1 {
public static void main (String [] args) {
runnable demo = new myrunable ();
스레드 T1 = 새 스레드 (Demo, "T1"); // T1은 Demo, 런닝 가능한 개체를 기반으로합니다.
스레드 T2 = 새 스레드 (Demo, "T2"); // "스레드 T2"를 만듭니다.
t1.start (); // 시작 "스레드 t1"
t2.start (); // 시작 "스레드 t2"
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T1 루프 0
T1 루프 1
T1 루프 2
T1 루프 3
T1 루프 4
T2 루프 0
T2 루프 1
T2 루프 2
T2 루프 3
T2 루프 4
결과 설명 :
run () 메소드에는 "동기화 된 (this) 코드 블록"이 있으며, T1과 T2는 "데모"런 가능 객체를 기반으로 작성된 스레드입니다. 이것은 우리가 이것을 동기화 된 (이것)로 "데모 런닝 가능한 객체"로 간주 할 수 있음을 의미합니다. 따라서 한 스레드가 실행될 때 다른 스레드는 "실행 스레드"가 실행되기 전에 "데모 동기화 잠금"을 해제 할 때까지 기다려야합니다.
확인하면이 문제를 알아 냈습니다. 그런 다음 위의 코드를 수정 한 다음 결과가 어떻게되는지 확인하고 혼란 스러울 지 확인합니다. 수정 된 소스 코드는 다음과 같습니다.
코드 사본은 다음과 같습니다.
클래스 Mythread는 스레드를 확장합니다 {
공개 신화 (문자열 이름) {
슈퍼 (이름);
}
@보수
public void run () {
동기화 (this) {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
공개 클래스 demo1_2 {
public static void main (String [] args) {
스레드 T1 = 새 신화 ( "T1"); // "스레드 T1"
스레드 T2 = 새로운 신화 ( "T2"); // 새 "스레드 T2"
t1.start (); // 시작 "스레드 t1"
t2.start (); // 시작 "스레드 t2"
}
}
코드 설명 :
DEMO1_2와 DEMO1_1을 비교하면 Demo1_2의 신화 클래스가 스레드에서 직접 상속되고 T1과 T2는 모두 신화 자식 스레드임을 발견했습니다.
다행히도 "run () demo1_2의 run () 메소드는 동기화 된 (this)라고도합니다.
그렇다면 Demo1_2의 실행 프로세스가 Demo1_1과 동일합니까?
실행 결과 :
코드 사본은 다음과 같습니다.
T1 루프 0
T2 루프 0
T1 루프 1
T2 루프 1
T1 루프 2
T2 루프 2
T1 루프 3
T2 루프 3
T1 루프 4
T2 루프 4
결과 설명 :
이 결과가 당신을 전혀 놀라게하지 않으면, 나는 당신이 동기화 된 것에 대해 더 깊이 이해하고 있다고 생각합니다. 그렇지 않으면 여기에서 분석을 계속 읽으십시오.
동기화 된 (이)에서는 "현재 클래스 객체", 즉 동기화 된 클래스 (this)가 위치한 클래스에 해당하는 현재 객체를 나타냅니다. 그 목적은 "현재 객체의 동기 잠금"을 얻는 것입니다.
Demo1_2의 경우, 이것은 동기화 된 (이)의 신화 객체를 나타내고, T1과 T2는 두 가지 다른 신화 객체 인 경우, T1은 동기화 (이)를 실행할 때 다른 객체의 동기화 잠금을 획득합니다. Demo1_1 쌍의 경우, 이는 동기화 된 객체를 나타내고 T2는 객체의 동기화 잠금을 획득합니다.
제 2 조
스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 여전히 "이 개체"의 비동기 코드 블록에 액세스 할 수 있습니다.
아래는 "동기화 된 코드 블록"에 해당하는 데모 프로그램입니다.
코드 사본은 다음과 같습니다.
클래스 카운트 {
// 동기화 된 동기화 블록을 포함하는 메소드
public void synmethod () {
동기화 (this) {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "SynMethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
// 비동기 방법
public void nonsynmethod () {
노력하다 {
for (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "NonsynMethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
공개 클래스 demo2 {
public static void main (String [] args) {
최종 카운트 카운트 = 새 카운트 ();
// 새 t1 생성, t1은 "count 객체"의 synmethod () 메소드를 호출합니다.
스레드 T1 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
count.synmethod ();
}
}, "t1");
// 새 t2 생성, t2는 "count 객체"의 nonsynmethod () 메소드를 호출합니다.
스레드 t2 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
count.nonsynmethod ();
}
}, "t2");
t1.start (); // 시작 t1
t2.start (); // 시작 t2
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T1 Synmethod 루프 0
T2 Nonsynmethod 루프 0
T1 Synmethod 루프 1
T2 Nonsynmethod 루프 1
T1 Synmethod 루프 2
T2 Nonsynmethod 루프 2
T1 Synmethod 루프 3
T2 Nonsynmethod 루프 3
T1 Synmethod 루프 4
T2 Nonsynmethod 루프 4
결과 설명 :
주 스레드에서 두 개의 새로운 자식 스레드 T1과 T2가 생성됩니다. T1은 Count Object의 SynMethod () 메소드를 호출하며, 동기화 블록이 포함되어 있습니다. T1이 실행될 때, 동기화 된 (이것)는 "count synchronization lock"을 얻기 위해 호출되지만 T2가 "count"동기화 잠금을 사용하지 않기 때문에 차단되지 않습니다.
제 3 조
스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 다른 "동기화 된 메소드"또는 "객체"의 "동기화 된 코드 블록"에 대한 액세스가 차단됩니다.
또한 위의 예에서 동기화 된 (this)에서 nonsynmethod () 메소드 본문을 수정합니다. 수정 된 소스 코드는 다음과 같습니다.
코드 사본은 다음과 같습니다.
클래스 카운트 {
// 동기화 된 동기화 블록을 포함하는 메소드
public void synmethod () {
동기화 (this) {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "SynMethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
// 동기화 된 동기화 블록 메소드도 포함됩니다
public void nonsynmethod () {
동기화 (this) {
노력하다 {
for (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentthread (). getName () + "NonsynMethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
공개 클래스 demo3 {
public static void main (String [] args) {
최종 카운트 카운트 = 새 카운트 ();
// 새 t1 생성, t1은 "count 객체"의 synmethod () 메소드를 호출합니다.
스레드 T1 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
count.synmethod ();
}
}, "t1");
// 새 t2 생성, t2는 "count 객체"의 nonsynmethod () 메소드를 호출합니다.
스레드 t2 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
count.nonsynmethod ();
}
}, "t2");
t1.start (); // 시작 t1
t2.start (); // 시작 t2
}
}
(한 번) 실행 결과 :
코드 사본은 다음과 같습니다.
synmethod () : 11
synblock () : 3
4. 인스턴스 잠금 및 글로벌 잠금
인스턴스 잠금-인스턴스 객체에서 잠금. 클래스가 싱글 톤이라면, 잠금 장치는 또한 글로벌 잠금의 개념을 가지고 있습니다.
동기화 된 키워드는 인스턴스 잠금에 해당합니다.
글로벌 잠금-이 잠금은 인스턴스의 객체 수에 관계없이 클래스를 대상으로합니다.
글로벌 잠금은 정적 동기화 된 (또는이 클래스의 클래스 또는 클래스 로더 객체에 잠겨 있음)에 해당합니다.
"인스턴스 잠금"및 "글로벌 잠금"의 매우 생생한 예가 있습니다.
코드 사본은 다음과 같습니다.
Pulbic Class 뭔가 {
공개 동기화 된 void issynca () {}
public synchronized void issyncb () {}
공개 정적 동기화 된 void csynca () {}
공개 정적 동기화 된 void csyncb () {}
}
무언가에 두 가지 인스턴스 x와 y가 있다고 가정 해 봅시다. 다음 4 개의 표현 세트로 획득 한 자물쇠를 분석하십시오.
(01) x.issynca () 및 x.issyncb ()
(02) x.issynca () 및 y.issynca ()
(03) x.csynca () 및 y.csyncb ()
(04) x.issynca () 및 무언가 .csynca ()
(01) 동시에 액세스 할 수 없습니다. issynca () 및 issyncb ()는 동일한 객체 (객체 x)에 액세스하는 동기화 잠금이기 때문입니다!
코드 사본은 다음과 같습니다.
// locktest2.java 소스 코드
클래스 뭔가 {
공개 동기화 된 void issynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public synchronized void issyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
공개 클래스 Locktest2 {
x = 새로운 것 ();
y = 새로운 것 ();
// y.issynca ()와 (02) x.issynca () 비교
개인 void test2 () {
// 새 T21을 생성하고 T21은 X.issynca ()를 호출합니다.
스레드 T21 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
x.issynca ();
}
}, "t21");
// 새 T22를 생성하고 T22는 x.issyncb ()를 호출합니다.
스레드 t22 = 새로운 스레드 (
새로운 runnable () {
@보수
public void run () {
y.issynca ();
}
}, "t22");
t21.start ();
T22.Start (); // 시작 T22
}
public static void main (String [] args) {
Locktest2 Demo = New Locktest2 ();
demo.test2 ();
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T11 : ISSYNCA
T11 : ISSYNCA
T11 : ISSYNCA
T11 : ISSYNCA
T11 : ISSYNCA
T12 : ISSYNCB
T12 : ISSYNCB
T12 : ISSYNCB
T12 : ISSYNCB
T12 : ISSYNCB
(02)는 동시에 액세스 할 수 있습니다. 동일한 객체의 동기화 잠금에 액세스하지 않기 때문에 x.issynca ()는 x의 동기화 잠금에 액세스하고 y.issynca ()는 y의 동기화 잠금에 액세스합니다.
코드 사본은 다음과 같습니다.
// locktest2.java 소스 코드
클래스 뭔가 {
공개 동기화 된 void issynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public synchronized void issyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
공개 클래스 Locktest2 {
x = 새로운 것 ();
y = 새로운 것 ();
// y.issynca ()와 (02) x.issynca () 비교
개인 void test2 () {
// 새 T21을 생성하고 T21은 X.issynca ()를 호출합니다.
스레드 T21 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
x.issynca ();
}
}, "t21");
// 새 T22를 생성하고 T22는 x.issyncb ()를 호출합니다.
스레드 t22 = 새로운 스레드 (
새로운 runnable () {
@보수
public void run () {
y.issynca ();
}
}, "t22");
t21.start ();
T22.Start (); // 시작 T22
}
public static void main (String [] args) {
Locktest2 Demo = New Locktest2 ();
demo.test2 ();
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T21 : ISSYNCA
T22 : ISSYNCA
T21 : ISSYNCA
T22 : ISSYNCA
T21 : ISSYNCA
T22 : ISSYNCA
T21 : ISSYNCA
T22 : ISSYNCA
T21 : ISSYNCA
T22 : ISSYNCA
(03)은 동시에 액세스 할 수 없습니다. csynca () 및 csyncb ()는 모두 정적 유형이기 때문에 x.csynca ()는 무언가와 동일하고 y.csyncb ()는 무언가.issyncb ()와 동일하므로 동기화 잠금을 공유하고 공유 할 수 없습니다. 동시에 질문을받습니다.
코드 사본은 다음과 같습니다.
// locktest3.java 소스 코드
클래스 뭔가 {
공개 동기화 된 void issynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public synchronized void issyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
공개 클래스 Locktest3 {
x = 새로운 것 ();
y = 새로운 것 ();
// y.csyncb ()와 비교 (03) x.csynca ()
개인 void test3 () {
// 새 T31을 생성하고 T31은 X.issynca ()를 호출합니다.
스레드 T31 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
x.csynca ();
}
}, "t31");
// 새 T32를 생성하고 T32는 X.issyncb ()를 호출합니다.
스레드 T32 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
y.csyncb ();
}
}, "t32");
t31.start ();
t32.start (); // 시작 t32
}
public static void main (String [] args) {
Locktest3 Demo = New Locktest3 ();
demo.test3 ();
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T31 : CSYNCA
T31 : CSYNCA
T31 : CSYNCA
T31 : CSYNCA
T31 : CSYNCA
T32 : CSYNCB
T32 : CSYNCB
T32 : CSYNCB
T32 : CSYNCB
T32 : CSYNCB
(04)에 동시에 액세스 할 수 있습니다. issynca ()는 인스턴스 메소드이기 때문에 x.issynca ()는 객체 x의 잠금을 사용하는 반면 csynca ()는 정적 방법입니다. 따라서 동시에 액세스 할 수 있습니다.
코드 사본은 다음과 같습니다.
// locktest4.java 소스 코드
클래스 뭔가 {
공개 동기화 된 void issynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public synchronized void issyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csynca () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
System.out.println (thread.currentthread (). getName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
공개 정적 동기화 된 void csyncb () {
노력하다 {
for (int i = 0; i <5; i ++) {
thread.sleep (100);
system.out.println (thread.currentthread (). getName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
공개 클래스 Locktest4 {
x = 새로운 것 ();
y = 새로운 것 ();
// 비교 (04) x.issynca ()와 무언가와 비교 .csynca ()
개인 void test4 () {
// 새 T41을 생성하고 T41은 X.issynca ()를 호출합니다.
스레드 T41 = 새 스레드 (
새로운 runnable () {
@보수
public void run () {
x.issynca ();
}
}, "t41");
// 새 T42를 생성하고 T42는 X.issyncb ()를 호출합니다.
스레드 T42 = 새로운 스레드 (
새로운 runnable () {
@보수
public void run () {
Something.csynca ();
}
}, "t42");
t41.start (); // 시작 t41
t42.start (); // 시작 t42
}
public static void main (String [] args) {
Locktest4 Demo = New Locktest4 ();
demo.test4 ();
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T41 : ISSYNCA
T42 : CSYNCA
T41 : ISSYNCA
T42 : CSYNCA
T41 : ISSYNCA
T42 : CSYNCA
T41 : ISSYNCA
T42 : CSYNCA
T41 : ISSYNCA
T42 : CSYNCA