스레드 상태 다이어그램
스레드에는 다음 5 개 상태가 포함됩니다.
1. 새로운 상태 : 스레드 객체가 생성 된 후 새 상태로 들어갑니다. 예를 들어, 스레드 스레드 = 새 스레드 ()입니다.
2. 런 가능 : "실행 가능 상태"라고도합니다. 스레드 객체가 생성 된 후 다른 스레드는 객체의 start () 메소드를 호출하여 스레드를 시작합니다. 예를 들어, Thread.Start (). 준비 상태의 스레드는 언제든지 CPU에 의해 실행 될 수 있습니다.
3. 실행 상태 (실행) : 스레드는 실행에 대한 CPU 권한을 얻습니다. 스레드는 준비 상태에서만 실행중인 상태 만 입력 할 수 있습니다.
4. 차단 된 상태 : 차단 된 상태는 스레드가 어떤 이유로 CPU 사용 권한을 포기하고 일시적으로 실행을 중지한다는 것을 의미합니다. 스레드가 준비 상태에 들어가기 전까지는 달리기 상태로 갈 기회가 없습니다. 막힘에는 세 가지 유형이 있습니다.
(01) 차단 대기 - 스레드의 Wait () 메소드를 호출하여 스레드가 특정 작업의 완료를 기다리게하십시오.
(02) 동기화 된 차단-스레드는 동기화 된 동기화 잠금을 얻지 못합니다 (잠금이 다른 스레드에 의해 점유되기 때문에) 동기화 된 차단 상태로 들어갑니다.
(03) 기타 차단-스레드는 스레드의 sleep () 또는 join ()을 호출하거나 I/O 요청을 발행하여 차단 상태로 들어갑니다. Sleep () State가 시간이 초과되면 join ()는 스레드가 끝나거나 시간이 끝나기를 기다렸거나 I/O 처리가 완료되면 스레드가 준비 상태에 다시 들어갔습니다.
5. Dead State : 스레드가 예외로 인해 Run () 메소드를 실행하거나 종료했으며 스레드는 수명주기를 종료합니다.
멀티 스레딩 메소드 스레드 및 실행 가능
글타래 (쓰레드) : 스레드 클래스 상속, 실행 메소드 구현 및 기본 함수의 시작 메소드를 호출하여 스레드를 시작합니다.
runnable : 인터페이스, 런닝 가능한 인터페이스를 구현하고, 매개 변수로 스레드 생성자로 전달하고, 메인에서 시작 메소드를 호출합니다.
예:
클래스 작업은 실행 가능한 {private int ticket = 10; @override public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {system.out.println (thread.currentThread (). getName () + "판매 된 티켓" + this.ticket-); }}}}}}; 공개 클래스 runnableTest {public static void main (String [] args) {task mytask = new Task (); 스레드 T1 = 새 스레드 (Mytask); 스레드 T2 = 새 스레드 (Mytask); 스레드 T3 = 새 스레드 (Mytask); t1.start (); t2.start (); t3.start (); }} // threadTest.java 소스 코드 클래스 Mythread는 스레드 {private int ticket = 10; public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {system.out.println (this.getName () + "티켓 판매 : 티켓" + this.ticket--); }}}} public class 스레드 테스트 {public static void main (String [] args) {// 3 스레드 시작 T1, T2, T3; 각 스레드는 각각 10 개의 티켓을 판매합니다! Mythread T1 = New Mythread (); Mythread T2 = New Mythread (); Mythread T3 = New Mythread (); t1.start (); t2.start (); t3.start (); }};
스레드와 실행 가능의 차이
스레드는 클래스이며, 런 가능성은 인터페이스입니다. 스레드 자체는 런닝 가능한 인터페이스를 구현하는 클래스입니다. 우리는 "클래스는 하나의 부모 클래스 만 가질 수 있지만 여러 인터페이스를 구현할 수 있습니다"라는 것을 알고 있으므로 Runnable은 확장 성이 향상됩니다. 또한 Runnable은 "자원 공유"에도 사용될 수 있습니다. 즉, 여러 스레드가 특정 실행 가능한 객체를 기반으로 생성되며 런닝 가능한 객체에 대한 리소스를 공유합니다. 일반적으로 "Runnable"을 통해 멀티 스레딩을 구현하는 것이 좋습니다!
스레드가 실행되고 시작됩니다
start () : 함수는 새 스레드를 시작하는 것이며 새 스레드는 해당 run () 메소드를 실행합니다. start ()는 반복적으로 호출 할 수 없습니다. start ()는 실제로 로컬 메소드 start0 ()을 통해 스레드를 시작합니다. start0 ()는 새 스레드를 실행하고 새 스레드는 run () 메소드를 호출합니다.
run () : run ()은 일반 멤버 메소드처럼 반복적으로 호출 할 수 있습니다. run ()을 별도로 호출하면 현재 스레드에서 run ()가 실행되며 새 스레드가 시작되지 않습니다! run ()은 스레드 스레드의 runnable 멤버의 run () 메소드를 직접 호출하고 새 스레드를 생성하지 않습니다.
// demo.java의 소스 코드 클래스 Mythread는 스레드 {public mythread (문자열 이름) {super (name); } public void run () {system.out.println (thread.currentThread (). getName ()+"is running"); }}; 공개 클래스 데모 {public static void main (String [] args) {Thread Mythread = new Mythread ( "Mythread"); System.out.println (thread.currentthread (). getName ()+"Call Mythread.run ()"); mythread.run (); System.out.println (thread.currentThread (). getName ()+"Call Mythread.start ()"); mythread.start (); }}산출:
Main Call Mythread.run () 메인은 RunningMain ice rund mythread.start () mythread가 실행 중입니다
동기화
Java에서는 각 객체에는 동기화 잠금이 있습니다. 객체의 동기화 된 메소드를 호출하면 객체 잠금이 획득되고 동기화 된 (OBJ)는 "OBJ 객체"의 동기화 잠금을 획득합니다. 동기화 잠금에 대한 다른 스레드 액세스는 상호 배타적입니다. 객체의 동기화 잠금은 특정 시간에 하나의 스레드로만 얻을 수 있습니다. 동기화 잠금을 통해 여러 스레드에서 "객체/방법"에 대한 상호 배타적 인 액세스를 얻을 수 있습니다. 예를 들어, 이제 두 개의 스레드 A와 스레드 B가 있으며, 이는 모두 "객체 OBJ의 동기화 된 잠금"에 액세스합니다. 어느 시점에서 Thread A가 "OBJ의 동기화 잠금"을 획득하고 일부 작업을 수행한다고 가정하십시오. 이때 스레드 B는 또한 "OBJ의 동기화 잠금"을 획득하려고 시도합니다. - 스레드 B는 획득하지 못하고 스레드 A가 "OBJ의 동기화 잠금 잠금"을 릴리스 할 때까지 기다려야하며 실행할 수 있습니다.
기본 규칙
제 1 조 : 스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 "동기화 된 메소드"또는 "객체"의 "동기화 된 코드 블록"에 대한 액세스에서 차단됩니다.
제 2 조 : 스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스 할 때 다른 스레드는 여전히 "이 개체"의 비동기 코드 블록에 액세스 할 수 있습니다.
제 3 조 : 스레드가 "특정 객체"의 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하면 다른 스레드는 "객체"의 다른 "동기화 된 메소드"또는 "동기화 된 코드 블록"에 액세스하지 못하게됩니다.
동기화 된 방법
public synchronized void foo1 () {system.out.println ( "synchronized method");} 동기화 된 코드 블록 공개 void foo2 () {synchronized (this) {system.out.println ( "synchronized method"); }}동기화 된 코드 블록에서 이것은 현재 객체를 나타냅니다. 이것은 또한 OBJ로 대체되는 다른 객체로 대체 될 수 있으며, Foo2 ()는 동기화 될 때 OBJ의 동기화 잠금을 획득합니다 (OBJ).
동기화 된 코드 블록은 충돌 제한 액세스 영역을보다 정확하게 제어 할 수 있으며 때로는보다 효율적으로 수행 할 수 있습니다.
인스턴스 잠금 및 글로벌 잠금
인스턴스 잠금-인스턴스 객체에서 잠금. 클래스가 싱글 톤이라면, 잠금 장치는 또한 글로벌 잠금의 개념을 가지고 있습니다. 동기화 된 키워드는 인스턴스 잠금에 해당합니다.
글로벌 잠금-이 잠금 장치는 클래스를 대상으로합니다. 인스턴스의 객체 수에 관계없이 스레드는 잠금을 공유합니다. 글로벌 잠금은 정적 동기화 된 (또는이 클래스의 클래스 또는 클래스 로더 객체에 잠겨 있음)에 해당합니다.
Pulbic Class Something {public synchronized void issynca () {} public synchronized void issyncb () {} public static synchronized void csynca () {} public static synchronized void csyncb () {}}
(01) x.issynca () 및 x.issyncb ()는 동시에 액세스 할 수 없습니다. issynca () 및 issyncb ()는 동일한 객체 (객체 x)에 액세스하는 동기화 잠금이기 때문입니다!
(02) x.issynca () 및 y.issynca ()는 동시에 액세스 할 수 있습니다. 동일한 객체의 동기화 잠금에 액세스하지 않기 때문에 x.issynca ()는 x의 동기화 잠금에 액세스하고 y.issynca ()는 y의 동기화 잠금에 액세스합니다.
(03) x.csynca () 및 y.csyncb ()는 동시에 액세스 할 수 없습니다. csynca () 및 csyncb ()는 모두 정적 유형이기 때문에 x.csynca ()는 무언가와 동일하고 y.csyncb ()는 무언가와 동일합니다. issyncb ()는 동기화 잠금을 공유하며 동시에 요청할 수 없습니다.
(04) x.issynca () 및 Something.csynca ()에 동시에 액세스 할 수 있습니다. issynca ()는 인스턴스 메소드이기 때문에 x.issynca ()는 객체 x의 잠금을 사용합니다. csynca ()는 정적 메소드이지만, csynca ()는 그것이 "클래스 잠금"이라는 것을 이해할 수 있습니다. 따라서 동시에 액세스 할 수 있습니다.
스레드 차단 및 웨이크 업 대기, 알림, NotifyAll
Object.java에서 Wait (), notify () 및 notifyall ()과 같은 인터페이스가 정의됩니다. Wait ()의 함수는 현재 스레드가 대기 상태로 들어가도록하는 것입니다. Wait ()도 현재 스레드가 보유한 잠금 장치를 해제하도록합니다. notify () 및 notifyall ()의 역할은 현재 객체의 대기 스레드를 깨우는 것입니다. notify ()는 단일 스레드를 깨우고 Notifyall ()은 모든 스레드를 깨우는 것입니다.
객체 클래스에서 대기/깨우기에 대한 API 세부 사항은 다음과 같습니다.
notify () -이 객체 모니터에서 대기 대기하는 단일 스레드를 깨우십시오.
notifyall () -이 객체 모니터에서 대기하는 모든 스레드를 깨우십시오.
대기 () - 현재 스레드를 "대기 (차단) 상태"에 넣고 "다른 스레드 가이 개체의 notify () 메소드 또는 notifyall () 메소드를 호출 할 때까지 현재 스레드가 깨어납니다 ("준비 상태 ").
대기 (Long Timeout) - 현재 스레드를 "대기 (차단) 상태"에 넣고 "다른 스레드가 객체의 notify () 메소드 또는 notifyall () 메소드를 호출하거나 지정된 시간을 초과 할 때까지 현재 스레드가 깨어납니다 ("준비 상태 "에 입력).
대기 (긴 타임 아웃, int nanos) - 현재 스레드가 "대기 (차단) 상태", "다른 스레드가 객체의 notify () 메소드 또는 notifyall () 메소드를 호출하거나 다른 스레드가 현재 스레드를 방해하거나 특정 시간을 초과 할 때까지"현재 스레드가 깨어났다 ( "준비 상태에 입력").
// WaitTest.java의 소스 코드 클래스 스레드는 스레드 {public threada (문자열 이름) {super (name); } public void run () {synchronized (this) {system.out.println (thread.currentthread (). getName ()+"call notify ()"); // 현재 대기 스레드를 깨우십시오. }}} public class waittest {public static void main (String [] args) {Threada t1 = new Threada ( "T1"); synchronized (t1) {try {// stark "strook t1"system.out.println (thread.currentthread (). getName ()+"start t1"); t1.start (); // 메인 스레드는 t1이 notify ()를 통해 깨어날 때까지 기다립니다. system.out.println (thread.currentthread (). getName ()+"Wait ()"); T1.WAIT (); system.out.println (thread.currentthread (). getName ()+"계속"); } catch (InterruptedException e) {e.printstacktrace (); }}}}산출
메인 시작 t1main wait () t1 call notify () main 계속
(01) 그림의 "메인 스레드"는 "메인 스레드 메인"을 나타냅니다. "스레드 T1"은 대기 테스트에서 시작된 "스레드 T1"을 나타냅니다. "잠금"은 "객체 T1의 동기 잠금"을 나타냅니다.
(02) "Main Thread"는 New Threada ( "T1")를 통해 새로운 "스레드 T1"을 만듭니다. 그런 다음 "T1 객체의 동기 잠금"은 동기화 된 (T1)를 통해 얻습니다. 그런 다음 t1.start ()를 호출하여 "스레드 t1"을 시작하십시오.
(03) "메인 스레드"는 t1.wait ()를 실행하여 "t1 객체의 잠금"을 해제하고 "대기 (차단) 상태"로 들어갑니다. t1 객체의 스레드가 Notify () 또는 notifyall ()을 통해 깨어날 때까지 기다립니다.
(04) "스레드 T1"이 실행 된 후, "현재 객체의 잠금"은 동기화 된 (this)를 통해 얻어진다; 그런 다음 Notify ()를 호출하여 "현재 객체의 대기 스레드", 즉 "메인 스레드"를 깨우십시오.
(05) "스레드 T1"이 완료된 후 "현재 객체의 잠금"을 해제하십시오. 그 후 즉시 "메인 스레드"는 "T1 객체의 잠금"을 획득 한 다음 실행됩니다.
t1.wait ()는 "Thread T1"을 통해 호출 된 Wait () 메소드이지만 t1.wait ()가 "Main Thread Main"에있는 곳은 있습니다. 기본 스레드는 "현재 스레드", 즉 실행 상태 인 T1.wait ()가 실행되기 전에해야합니다. 따라서 현재 "현재 스레드"는 "메인 스레드 메인"입니다! 따라서 t1.wait ()는 "스레드 t1이 아닌"메인 스레드 "를 대기해야합니다!
패키지 스레드. 테스트; 공개 클래스 NotifyAllTest {private static 객체 obj = new Object (); public static void main (String [] args) {Threada t1 = new Threada ( "T1"); Threada t2 = 새로운 Threada ( "T2"); Threada t3 = 새로운 Threada ( "T3"); t1.start (); t2.start (); t3.start (); try {System.out.println (Thread.currentThread (). getName ()+"sleep (3000)"); Thread.sleep (3000); } catch (InterruptedException e) {e.printstacktrace (); } synchronized (obj) {system.out.println (thread.currentthread (). getName ()+"notifyall ()"); obj.notifyall (); // wake up t1.t2.t3 here}} 정적 클래스 스레드는 스레드 {public threada (string name) {super (name); } public void run () {synchronized (obj) {try {// printout result system.out.println (Thread.currentThread (). getName () + "대기"); // obj 객체 잠금을 릴리스 해제 obj.wait (); // printout result system.out.println (Thread.currentThread (). getName () + "계속"); } catch (InterruptedException e) {e.printstacktrace (); }}}}}} 산출:
T1 Waitmain Sleep (3000) T3 Waitt2 Waittmain NotifyAll () T2 계속 3 계속 1 계속
(01) 3 스레드 "T1", "T2"및 "T3"가 작성되어 기본 스레드에서 시작되었습니다.
(02) 메인 스레드는 수면을 통해 3 초 동안 잠을 자고 있습니다 (3000). 메인 스레드가 3 초 동안 수면 중에, 우리는 3 개의 스레드 "T1", "T2"및 "T3"이 모두 실행되고 있다고 가정합니다. "T1"을 예로 들어 보겠습니다. 실행되면 Obj.wait ()가 실행되면 다른 스레드가 Notify () 또는 nofityall ()을 통해 깨어날 때까지 기다립니다. 마찬가지로 "T2"및 "T3"도 다른 스레드가 Nofity () 또는 NofityAll ()을 통해 깨어날 때까지 기다립니다.
(03) 메인 스레드는 3 초 동안 자고 실행됩니다. obj.notifyall ()을 실행하여 OBJ의 대기 실을 깨우십시오. 기본 스레드의 동기화 (OBJ)가 실행 된 직후 기본 스레드는 "OBJ 잠금"을 방출합니다. 이러한 방식으로 "T1", "T2"및 "T3"은 "OBJ Lock"을 얻고 계속 실행할 수 있습니다!
알림, 알림 및 잠금 관계
wait (), notify ()와 같은 함수는 동기화 된 것과 같이 "객체 동기화 잠금"에서 작동합니다.
대기 ()는 "현재 스레드"를 기다립니다. 스레드가 대기 상태로 들어가기 때문에 스레드는 잠금으로 고정 된 "동기 잠금"을 해제해야합니다. 그렇지 않으면 다른 스레드는 "동기 잠금"을 얻지 못하며 실행할 수 없습니다!
자, 스레드 호출 대기 () 후에는 잠금 장치가 보유한 "동기 잠금"을 해제합니다. 그리고 이전 소개에 따르면, 우리는 대기 스레드가 notify () 또는 notifyall ()에 의해 깨어날 수 있음을 알고 있습니다. 이제 질문에 대해 생각해보십시오. 또는 Wait ()와 notify ()의 상관 관계는 무엇입니까? 대답은 "객체 동기화 잠금"을 기반으로합니다.
대기 스레드를 깨우는 스레드 (우리는 "Wake Up Thread"라고 부르며, "이 객체의 동기 잠금"을 얻은 후 대기 스레드를 깨울 수 있습니다 (여기서 동기화 잠금은 대기 스레드의 동기화 잠금 잠금과 동일해야합니다). 그러나 대기 실이 깨어났다. 그러나 웨이크 업 스레드가 여전히 "객체의 동기 잠금"을 유지하기 때문에 즉시 실행할 수 없습니다. "오브젝트의 동기화 잠금"을 얻고 계속 실행되기 전에 웨이크 업 스레드가 "객체의 동기화 잠금"을 풀 때까지 기다려야합니다.
요컨대, Notify (), wait ()는 객체 잠금으로 고정 된 "동기 잠금"에 의존하며 각 객체는 하나만 있습니다! 그렇기 때문에 Notify (), wait ()와 같은 함수가 스레드 클래스가 아닌 객체 클래스에 정의됩니다.
스레드 양보 수율
스레드 양보는 스레드를 실행 상태에서 준비 상태로 변경하여 우선 순위가 동일한 다른 대기 스레드가 실행 권한을 얻을 수 있도록합니다. 그러나 현재 스레드가 axption ()을 호출 한 후에는 동일한 우선 순위를 가진 다른 스레드가 분명히 실행 권한을 얻는다는 것은 보장되지 않습니다. 현재 스레드가 "실행 상태"로 들어가서 계속 실행될 수도 있습니다.
양보하고 기다립니다
(01) 대기 ()는 스레드가 "실행 상태"에서 "대기 (차단) 상태"로 들어가게하는 반면, 율은 없음 ()은 스레드가 "실행 된 상태"에서 "Ready State"에 들어가도록하는 것입니다.
(02) Wait ()는 보유한 객체를 실행하는 스레드를 방출하는 동기화 잠금 장치이며, 수율 () 메소드는 잠금을 해제하지 않습니다.
(03) 대기는 물체의 방법입니다. 수율은 실의 방법입니다.
실로 수면
Sleep ()의 기능은 현재 스레드 수면, 즉 현재 스레드가 "실행 상태"에서 "수면 (차단) 상태"로 들어가게하는 것입니다. 수면 ()은 수면 시간을 지정하며 실로 수면 시간은 수면 시간보다/동일합니다. 스레드가 다시 깨어나면 CPU가 실행되기를 기다리는 "차단 상태"에서 "준비 상태"로 변경됩니다.
수면과 대기의 차이
Wait ()의 함수는 현재 스레드가 "실행 상태"에서 "대기 (차단) 상태로 들어가고 동기화 잠금을 해제하도록하는 것입니다. Sleep ()의 함수는 현재 스레드가"실행 상태 "에서"sleep (차단) 상태에 들어가도록하는 것입니다. (이것은 실제로 크게 다르지 않습니다)
대기 ()는 물체의 동기화 잠금을 풀고 sleep ()는 잠금을 해제하지 않습니다.
대기는 물체의 방법입니다. 수면은 실의 방법입니다.
가입하다
메인 스레드가 기다리고 메인 스레드가 완료된 후에도 하위 스레드가 계속 실행될 수 있습니다.
방해하다
차단 된 스레드를 종료하는 데 사용됩니다
@OverRidePublic void run () {try {while (true) {// 작업 실행 ...}} catch (InterpruptedException) {// InterruptedException Exception, Exit the the (true) 루프와 스레드가 종료됩니다! }}(true)에서 스레드의 인터럽트 () 호출은 인터럽트 xception 인터럽트를 생성합니다. 중단 된 캡처는 외부에 있으며 (true), while (true) 루프를 종료합니다.
실행 상태에서 스레드를 종료합니다
@overridepublic void run () {while (! islerrupted ()) {// execute task ...}}스레드를 종료하는 일반적인 방법
@overridepublic void run () {try {// 1. istinerrupted ()는 인터럽트가 사실이 표시되는 한 스레드가 종료되도록 보장합니다. while (! istrupted ()) {// execute task ...}} catch (InterpruptedException) {// 2. InterpruptedException 예외는 인터럽트 예외 예외가 발생하면 스레드가 종료되도록 보장합니다. }}
스레드 우선 순위
Java의 스레드 우선 순위 범위는 1 ~ 10이고 기본 우선 순위는 5입니다. "높은 우선 순위 스레드"는 "낮은 우선 순위 스레드"보다 실행됩니다. Java에는 사용자 스레드와 데몬 스레드의 두 가지 유형이 있습니다. isdaemon () 메소드로 구별 될 수 있습니다. 거짓이 반환되면 스레드가 "사용자 스레드"임을 의미합니다. 그렇지 않으면 "데몬 스레드"입니다. 사용자 스레드는 일반적으로 사용자 수준의 작업을 수행하는 반면 데몬 스레드는 "백엔드 스레드"이며 일반적으로 배경 작업을 수행하는 데 사용됩니다. "사용자 스레드"가 완료된 후 Java 가상 머신이 종료 될 것입니다.
각 스레드는 우선 순위가 있습니다. "높은 우선 순위 스레드"는 "낮은 우선 순위 스레드"보다 실행됩니다. 각 스레드는 데몬 또는 비 데몬으로 표시 될 수 있습니다. 실행 중 일부 메인 스레드에서 새 하위 스레드를 만들 때, 자식 스레드의 우선 순위는 "생성 된 기본 스레드의 우선 순위"와 "하위 스레드는 데몬 스레드"라는 경우에만 "하위 스레드는 데몬 스레드가됩니다"라고 설정됩니다.
Java 가상 머신이 시작되면 일반적으로 단일 비 데몬 스레드가 있습니다 (이 스레드는 Main () 메소드를 통해 시작됩니다). JVM은 다음 조건 중 하나가 발생할 때까지 실행되며 JVM은 실행을 종료합니다.
(01) exit () 메소드가 호출되고 exit ()는 정상적으로 실행할 수있는 권한이 있습니다.
(02) 모든 "비 데몬 스레드"는 죽었다 (즉, JVM에는 "데몬 스레드 만있다).
악마
(01) 메인 스레드 메인은 사용자 스레드이고, 자식 스레드 T1도 사용자 스레드입니다.
(02) T2는 데몬 스레드입니다. "메인 스레드 메인"및 "서브 스레드 T1"(둘 다 사용자 스레드 임)이 실행되고 데몬 스레드 T2 만 남으면 JVM이 자동으로 종료됩니다.
생산자 및 소비자 문제
(01) 생산자는 창고가 가득 차 있지 않은 경우에만 생산하고 창고가 가득 차면 생산을 중지합니다.
(02) 소비자는 스토리지에 제품이있을 때만 소비 할 수 있으며 빈 창고가있는 경우 기다릴 수 있습니다.
(03) 소비자가 창고에 소비 할 제품이 없다는 것을 알게되면 생산자에게 알릴 것입니다.
(04) 생산자가 소모품 제품을 생산할 때 대기 소비자에게 소비하도록 통지해야합니다.
스레드 간의 커뮤니케이션
방법 : 공유 메모리 및 메시징
공유 메모리 : 스레드 a 및 스레드 B 공유 메모리, 스레드 A는 공유 변수의 값을 업데이트하고 주 메모리로 새로 고침되며 스레드 B는 메인 메모리로 이동하여 스레드 A의 업데이트 된 변수를 읽습니다. 전체 통신 프로세스는 기본 메모리를 통과해야합니다. 동기화는 명시 적으로 수행됩니다.
변수가 휘발성 유형 인 경우 변수의 읽기 및 쓰기는 원자가됩니다. 휘발성 ++와 유사한 여러 휘발성 작업 또는 복합 작업 인 경우 이러한 작업은 전체적으로 원자가 아닙니다.
휘발 변수 자체에는 다음과 같은 특성이 있습니다.
[가시성] : 휘발성 변수를 읽을 때 항상 휘발성 변수 (모든 스레드)에 마지막 쓰기를 볼 수 있습니다.
[Atomicity] : 단일 휘발 변수의 읽기/쓰기에 대한 원자력이 있지만 휘발성 ++와 유사한 복합 작업에 대한 원자력이 없습니다.
휘발성 쓰기 : 휘발성 변수를 작성할 때 JMM은 스레드에 해당하는 로컬 메모리의 공유 변수를 기본 메모리에 플러시합니다.
휘발성 읽기 : 휘발성 변수를 읽을 때 JMM은 스레드에 해당하는 로컬 메모리를 무효화합니다. 스레드는 다음으로 주 메모리에서 공유 변수를 읽습니다.
메시지 전달 : 메시지 전송은 메시지가 수락되기 전에 암시 적으로 수행됩니다.
ThreadLocal
ThreadLocal은 공유 객체에 대한 멀티 스레드 액세스 문제를 해결하는 데 사용되지 않습니다. 일반적으로, ThreadLocal.set ()을 통해 스레드의 객체는 스레드 자체가 사용하는 객체입니다. 다른 스레드에 액세스 할 필요가 없으며 액세스 할 수 없습니다. ThreadLocal은 각 스레드가 자체 독립 객체를 유지할 수 있도록합니다. ThreadLocal.set ()을 통해 구현되지는 않지만 각 스레드에서 새 개체의 작동에 의해 생성 된 객체입니다. 각 스레드는 객체의 사본이나 사본이 아닌 하나를 만듭니다. 새로 생성 된 객체에 대한 참조는 ThreadLocal.set ()를 통해 각 스레드의 자체 맵에 저장됩니다. 각 스레드에는 그러한 맵이 있습니다. ThreadLocal.get ()가 실행되면 각 스레드는 자체 맵에서 배치 된 객체를 꺼냅니다. 따라서 꺼내는 것은 각 스레드의 객체입니다. ThreadLocal 인스턴스는 맵 키로 사용됩니다. ThreadLocal.set ()가 들어가는 것이 여러 스레드에서 공유되는 동일한 객체 인 경우 여러 스레드의 threadLocal.get ()는 여전히 공유 객체 자체를 얻고 동시 액세스 문제가 여전히 있습니다.
ThreadLocal의 구현
java.util.collections import; java.util.hashmap import; java.util.map import; /** * ThreadLocal * @author leizhimin 2010-5 10:35:27 */public class mythreadlocal {// int 또는 intger data private com.lavasoft.test2.threadlocal <integer> tl = new com.lavasoft.test2.threadlgerater protectation <integerated <integerater <integer data private com.lavasoft.test2. integer initialValue () {return 0; }}; public integer getnextnum () {// tl의 값을 얻고 1을 추가하고 t1 tl.set (tl.get () + 1)의 값을 업데이트합니다. return tl.get (); }} class ThreadLocal <t> {private map <Thread, t> map = collections.synchronizedMap (new Hashmap <Thread, t> ()); public ThreadLocal () {} protected t initialValue () {return null; } public t get () {Thread T = Thread.CurrentThread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } public void set (t value) {map.put (Thread.currentThread (), value); } public void remove () {map.remove (Thread.currentThread ()); }}실제로 ThreadLocal은 다음을 수행합니다.
public t get () {Thread T = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) {ThreadLocalMap.entry e = map.getEntry (this); if (e! = null) return (t) e.Value; } return setInitialValue (); }