0. MUTEX 정보
소위 MUTEX 잠금 장치는 한 번에 하나의 스레드 만 가질 수있는 잠금 장치를 나타냅니다. JDK1.5 이전에, 우리는 일반적으로 동기화 된 메커니즘을 사용하여 여러 스레드의 공유 리소스 액세스를 제어했습니다. 이제 Lock은 동기화 된 메커니즘보다 더 넓은 범위의 잠금 작업을 제공합니다. 잠금 장치와 동기화 된 메커니즘의 주요 차이점 :
동기화 된 메커니즘은 각 객체와 관련된 암시 적 모니터 잠금에 대한 액세스를 제공하고 모든 잠금 획득 및 릴리스가 블록 구조에 나타나도록 강요합니다. 다중 잠금 장치가 취득되면 역 순서로 해제해야합니다. 동기화 된 메커니즘은 잠금을 암시 적으로 방출합니다. 스레드에 의해 코드가 실행되는 한 동기화 된 명령문 블록의 범위를 초과하는 한 잠금이 해제됩니다. 잠금 메커니즘은 잠금 객체의 잠금 해제 () 메소드를 명시 적으로 호출하여 잠금을 해제해야하며, 이는 동일한 블록 구조에 나타나지 않을 수있는 잠금의 획득 및 해제 가능성을 제공하고 더 자유 순서로 잠금을 해제해야합니다.
1. 재진입 락 소개
ReintrantLock은 "독점 잠금"이라고도하는 재입국 뮤텍스 잠금 장치입니다.
이름에서 알 수 있듯이, 재진입 락크 잠금은 동일한 시점에서 하나의 스레드 잠금으로 만 유지할 수 있습니다. 재진입은 단일 스레드에 의해 재입국 락 잠금을 여러 번 수집 할 수 있음을 의미합니다.
ReintrantLock은 "Fair Lock"및 "Forair Lock"으로 나뉩니다. 그들의 차이는 자물쇠를 얻는 메커니즘이 공정한지 여부에 반영됩니다. "잠금"은 경쟁 자원을 보호하고 여러 스레드가 동시에 작동하는 스레드와 오류를 방지하는 것입니다. ReintrantLock은 동시에 하나의 스레드로만 획득 할 수 있습니다 (스레드가 "잠금"을 획득하면 다른 스레드가 대기해야합니다). ReintraantLock은 FIFO 대기 큐를 통해 잠금을 얻는 모든 스레드를 관리합니다. "Fair Lock"의 메커니즘 아래에서, 스레드는 큐를 순서대로 얻습니다. "비-아웃 잠금"은 큐의 시작에 관계없이 잠금을 얻습니다.
재진입 락 함수 목록
// 기본적으로 "불공평 한 잠금"인 ReentrantLock을 만듭니다. ReintrantLock () // 창조 정책은 박람회의 재입국입니다. 박람회가 사실이라면, 그것은 공정한 자물쇠라는 것을 의미하며, 박람회가 거짓이라면, 그것은 비공식 자물쇠임을 의미합니다. ReintrantLock (부울 박람회) // 현재 스레드 가이 잠금을 유지 한 횟수를 쿼리합니다. int getholdcount () // 현재이 잠금 장치를 소유 한 스레드를 반환 하며이 잠금 장치가 스레드가 소유하지 않으면 NULL을 반환합니다. Protected Thread Getowner () //이 잠금을 얻기 위해 대기 할 수있는 스레드가 포함 된 컬렉션을 반환합니다. 보호 된 컬렉션 <thread> getqueuedthreads () //이 잠금을 얻기 위해 대기하는 예상 스레드 수를 반환합니다. int getqueuelenger () //이 잠금과 관련된 주어진 조건을 기다릴 수있는 스레드가 포함 된 컬렉션을 반환합니다. 보호 된 수집 <thread> getwaitingthreads (조건 조건) //이 잠금과 관련된 주어진 조건을 기다리는 스레드 추정치를 반환합니다. int getwaitqueuelenger (조건 조건) // 쿼리 주어진 스레드 가이 잠금을 획득하기 위해 대기 중인지 여부. 부울 hasqueuedThread (스레드 스레드) // 일부 스레드 가이 잠금을 획득하기 위해 대기 중인지 쿼리합니다. 부울 hasqueuedthreads () // 쿼리 일부 스레드 가이 잠금과 관련된 주어진 조건을 기다리고 있는지 여부. 부울 haswaiters (조건 조건) // "공정한 잠금"인 경우 true를 반환하고 그렇지 않으면 False를 반환합니다. Boolean isfair () // 쿼리 현재 스레드 가이 잠금을 유지하는지 여부. 부울 isheldbycurrentthread () // 쿼리이 잠금 장치가 스레드에 의해 유지되는지 여부. 부울 islocked () // 잠금을 얻습니다. void lock () // 현재 스레드가 중단되지 않으면 잠금이 획득됩니다. void lockinterruptily () //이 잠금 인스턴스와 함께 사용하는 조건 인스턴스를 반환합니다. 조건 NewCondition () // 호출 중에 다른 스레드에서 유지되지 않은 경우 잠금을 얻습니다. 부울 trylock () // 주어진 대기 시간 내에 다른 스레드에 의해 잠금이 유지되지 않고 현재 스레드가 중단되지 않으면 잠금이 획득됩니다. 부울 트리 로크 (긴 타임 아웃, 시간 유닛) //이 잠금을 해제하려고합니다. 무효 잠금 해제 ()
2. 재진입 락 예제
"예제 1"과 "예제 2"를 비교하여 잠금 및 잠금 해제의 역할을 명확하게 이해할 수 있습니다.
2.1 예 1
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; // locktest1.java// repository class depot {private int size; // 실제 리포지토리 개인 잠금 잠금 수; // 독점 잠금 공공 저장소 () {this.size = 0; this.lock = new ReintrantLock (); } public void produce (int val) {lock.lock (); 시도 {size += val; System.out.printf ( "%s prods (%d) -> size =%d/n", thread.currentthread (). getName (), val, size); } 마침내 {lock.unlock (); }} public void 소비 (int val) {lock.lock (); 시도 {size- = val; System.out.printf ( "%s 소비 (%d) <- size =%d/n", thread.currentthread (). getName (), val, size); } 마침내 {lock.unlock (); }}}; // 프로듀서 클래스 프로듀서 {개인 창고 예금; 공공 생산자 (Depot Depot) {this.depot = 예금; } // 소비자 제품 : 창고에 제품을 생산하기 위해 새 스레드를 만듭니다. public void produce (final int val) {new Thread () {public void run () {eposit.proctord (val); } }.시작(); }} // 소비자 클래스 고객 {개인 창고 예금; 공개 고객 (Depot Depot) {this.depot = 예금; } // 소비자 제품 : 창고에서 제품을 소비 할 새 스레드를 만듭니다. public void 소비 (Final Int Val) {new Thread () {public void Run () {depot.consume (val); } }.시작(); }} public class locktest1 {public static void main (String [] args) {depot mdepot = new depot (); 생산자 mpro = 새로운 생산자 (mdepot); 고객 MCUS = 신규 고객 (MDEPOT); mpro.proctor (60); mpro.proctor (120); MCUS.Consume (90); MCUS.Consume (150); mpro.proctor (110); }} 실행 결과 :
Thread-0 Produce (60)-> size = 60thread-1 생산 (120)-> size = 180thread-3 소비 (150) <-크기 = 30thread-2 sopume (90) <-크기 = -60thread-4 product (110)-> size = 50
결과 분석 :
(1) 창고는 창고입니다. 상품은 Produce ()를 통해 창고로 생산 될 수 있으며 창고의 상품은 소비를 통해 소비 할 수 있습니다 (). 독점적 인 잠금 잠금 장치를 통해 창고에 대한 상호 배타적 인 액세스는 다음과 같습니다. 창고 (생산/소비)에서 상품을 운영하기 전에 창고는 먼저 잠금 ()을 통해 잠금을 겪은 다음 작동이 완료된 후 잠금 해제 ()를 통해 잠금 해제됩니다.
(2) 프로듀서는 생산자입니다. 생산자에서 Produce () 기능을 호출하면 창고에서 제품을 생산하기 위해 새로운 스레드를 만들 수 있습니다.
(3) 고객은 소비자 범주입니다. 고객에게 소비 () 기능을 호출하면 창고에서 새로운 스레드 소비 제품을 만들 수 있습니다.
(4) 메인 스레드 메인에서 우리는 새로운 생산자 MPRO와 새로운 소비자 MCU를 만들 것입니다. 그들은 각각 창고에 제품을 생산/소비합니다.
Main의 생산/소비량에 따르면, 창고의 최종 남은 제품은 50이어야합니다. 운영 결과는 우리의 기대와 일치합니다!
이 모델에는 두 가지 문제가 있습니다.
(1) 실제로 창고의 용량은 부정적 일 수 없습니다. 그러나이 모델의 창고 용량은 부정적 일 수 있으며, 이는 현실과 모순됩니다!
(2) 실제로 창고의 용량은 제한적이다. 그러나이 모델의 용량에는 실제로 제한이 없습니다!
우리는이 두 가지 문제를 해결하는 방법에 대해 간단히 이야기 할 것입니다. 이제 간단한 예제 2를 먼저 살펴 보겠습니다. "예제 1"과 "예제 2"를 비교함으로써 Lock () 및 Unlock ()의 목적을보다 명확하게 이해할 수 있습니다.
2.2 예제 2
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; // lektest2.java// repository class depot {private int size; // 실제 리포지토리 개인 잠금 잠금 수; // 독점 잠금 공공 저장소 () {this.size = 0; this.lock = new ReintrantLock (); } public void produce (int val) {// lock.lock (); // try {size += val; System.out.printf ( "%d) -> size =%d/n", thread.currentThread (), getName (), val, size); //} catch (InterpruptedExcept e) {//} 최종적 {// lock.unlock (); //}} public void 소비 (int val) {// rock.lock (); System.out.printf ( "%s 소비 (%d) <- size =%d/n", thread.currentthread (). getName (), val, size); //} 최종적 {// lock.unlock (); //}}; // 생산자 클래스 생산자 {private depot epostit; 공공 생산자 (Depot Jeptit) {this.depot = 예금; } // 소비자 제품 : 제품을 창고에 생산하기 위해 새 스레드를 만듭니다. public void produce (final int val) {new Thread () {public void run () {eposit.proctord (val); } }.시작(); }} // 소비자 클래스 고객 {개인 창고 예금; 공개 고객 (Depot Depot) {this.depot = 예금; } // 소비자 제품 : 창고에서 제품을 소비 할 새 스레드를 만듭니다. public void 소비 (Final Int Val) {new Thread () {public void Run () {depot.consume (val); } }.시작(); }} public class locktest2 {public static void main (String [] args) {depot mdepot = new depot (); 생산자 mpro = 새로운 생산자 (mdepot); 고객 MCUS = 신규 고객 (MDEPOT); mpro.proctor (60); mpro.proctor (120); MCUS.Consume (90); MCUS.Consume (150); mpro.proctor (110); }} (한 번) 결과 :
스레드 -0 농산물 (60)-> size = -60thread-4 생산물 (110)-> size = 50thread-2 소비 (90) <-크기 = -60thread-1 생산 (120)-> 크기 = -60thread-3 소비 (150) <-크기 = -60
결과 설명 :
"예 2"는 "예제 1"에 따라 잠금 잠금 장치를 제거합니다. 예 2에서, 창고의 최종 남은 제품은 -60이며, 우리가 예상 한 50 개가 아닙니다. 그 이유는 저장소에 대한 Mutex 액세스를 구현하지 않기 때문입니다.
2.3 예제 3
"예제 3"에서는 "예제 1"에서 두 가지 문제를 해결하기 위해 조건을 사용합니다. "창고의 용량은 부정적 일 수 없습니다"와 "창고의 용량은 제한적입니다.
이 문제에 대한 해결책은 조건을 통하는 것입니다. 조건은 잠금과 함께 사용해야합니다. 조건의 Await () 메소드는 스레드가 대기 (Waite ()와 유사하게 차단 될 수 있습니다. 조건의 신호 () 메소드는 웨이크 업 스레드가 [notify ()]로 유발 될 수 있습니다.
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; import java.util.concurrent.locks.condition; // waryhouse class depot {개인 int 용량; // 창고 용량 개인 int 크기; // 창고의 실제 잠금 잠금 장치 수; // 독점 잠금 개인 조건 FullCondition; // 생산 조건 개인 조건 빈 공학; // 소비 조건 공공 창고 (int 용량) {this.capacity = 용량; this.size = 0; this.lock = new ReintrantLock (); this.fullcondtion = lock.newcondition (); this.emptycondition = lock.newcondition (); } public void produce (int val) {lock.lock (); try {// 왼쪽은 "생산하려는 수량"을 의미합니다 (생산이 너무 많아서 더 많이 생산해야 할 수도 있습니다) int left = val; (왼쪽> 0) {// 인벤토리가 가득 차면 "소비자"가 제품을 소비 할 때까지 기다립니다. while (size> = faction) fullCondtion.await (); // "실제 생산 수량"(즉, 재고에 추가 된 새로운 수량)을 얻습니다. // "인벤토리" + "원하는 생산 수량"> "총 용량", 그런 다음 "실제 증분"= "총 용량" - "현재 용량". // 그렇지 않으면 "실제 증분"= "int Inc = (size+left)> 용량? (용량 크기) : 왼쪽; 크기 += inc; 왼쪽 -= inc; System.out.printf ( "%3d) -> 왼쪽 =%3d, inc =%3d, size =%3d/n", thread.currentthread (). getName (), val, left, inc, size); // "소비자"에게 소비 할 수 있음을 알립니다. emptyCondtion.signal (); }} catch (InterruptedException e) {} 최종적으로 {lock.unlock (); }} public void 소비 (int val) {lock.lock (); try {// 왼쪽은 "소비 할 소비량"을 의미합니다. (왼쪽> 0) {// 인벤토리가 0 일 때 "생산자"가 제품을 생산할 때까지 기다립니다. while (size <= 0) emptyCondtion.await (); // "실제 소비량"(즉, 실제 재고 감소)을 얻습니다. // "인벤토리"< "고객이 소비하고자하는 수량", "실제 소비"= "인벤토리"; // 그렇지 않으면, "실제 소비"= "고객이 소비하고자하는 수량". int dec = (size <왼쪽)? 크기 : 왼쪽; 크기 -= 12 월; 왼쪽 -= 12 월; System.out.printf ( "%s 소비 (%3d) <- 왼쪽 =%3d, dec =%3d, size =%3d/n", thread.currentthread (). getName (), val, 왼쪽, dec, size); fullCondtion.signal (); }} catch (InterruptedException e) {} 최종적으로 {lock.unlock (); }} public string toString () {return "faction :"+용량+", 실제 크기 :"+크기; }}; // 프로듀서 클래스 프로듀서 {개인 창고 예금; 공공 생산자 (Depot Jeptit) {this.depot = 예금; } // 소비자 제품 : 제품을 창고에 생산하기 위해 새 스레드를 만듭니다. public void produce (final int val) {new Thread () {public void run () {eposit.proctord (val); } }.시작(); }} // 소비자 클래스 고객 {개인 창고 예금; 공개 고객 (Depot Depot) {this.depot = 예금; } // 소비자 제품 : 창고에서 제품을 소비 할 새 스레드를 만듭니다. public void 소비 (Final Int Val) {new Thread () {public void Run () {depot.consume (val); } }.시작(); }} public class locktest3 {public static void main (String [] args) {Depot mdepot = new Depot (100); 생산자 mpro = 새로운 생산자 (mdepot); 고객 MCUS = 신규 고객 (MDEPOT); mpro.proctor (60); mpro.proctor (120); MCUS.Consume (90); MCUS.Consume (150); mpro.proctor (110); }} (한 번) 결과 :
스레드 -0 Produce (60)-> 왼쪽 = 0, INC = 60, 크기 = 60thread-1 생산 (120)-> 왼쪽 = 80, Inc = 40, 크기 = 100thread-2 소비 (90) <-왼쪽 = 0, dec = 90, 크기 = 10thread-3 소비 (150) <-왼쪽 = 140, dec = 10, size = 0thread-4 product (110). 소비 (150) <-왼쪽 = 40, dec = 100, size = 0thread-4 produce (110)-> 왼쪽 = 0, Inc = 10, size = 10thread-3 socume (150) <-왼쪽 = 30, dec = 10, size = 0thread-1 생산 (120)-> 왼쪽 = 0, inc = 80, size = 80thread-3 COUTUME (150) COUCE