[High Concurrency Java II] 멀티 스레딩 재단에서 처음에는 기본 스레드 동기화 작업을 언급했습니다. 이번에 언급하고 싶은 것은 동시 패키지의 동기화 제어 도구입니다.
1. 다양한 동기화 제어 도구 사용
1.1 Reintrantlock
ReintrantLock은 강화 된 버전의 동기화 된 것 같습니다. 동기화 된 특징은 사용이 간단하고 처리를 위해 JVM에 모든 것이 맡겨져 있지만 그 기능은 비교적 약하다는 것입니다. JDK1.5 이전에 ReintrantLock의 성능은 동기화 된 것보다 낫습니다. JVM의 최적화로 인해 현재 JDK 버전에서 두 가지의 성능이 비슷합니다. 간단한 구현이라면 의도적으로 ReentrantLock을 사용하지 마십시오.
동기화 된 것과 비교할 때, ReintrantLock은 기능적으로 풍부하며 재진입, 중단, 제한된 시간 및 공정한 잠금의 특성이 있습니다.
먼저 예제를 사용하여 ReintrantLock의 초기 사용법을 설명하겠습니다.
패키지 테스트; import java.util.concurrent.locks.reentrantlock; 공개 클래스 테스트 도구 런닝 가능 {public static reintrantlock lock = new ReintrantLock (); 공개 정적 int i = 0; @override public void run () {for (int j = 0; j <100000000; j ++) {lock.lock (); {i ++; } 마침내 {lock.unlock (); }}} public static void main (string [] args)은 중단 exception {test test = new test (); 스레드 T1 = 새 스레드 (테스트); 스레드 T2 = 새 스레드 (테스트); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }}i에 ++ 작업을 수행하는 두 개의 스레드가 있습니다. 스레드 안전을 보장하기 위해 ReintrantLock이 사용됩니다. 사용에서 우리는 동기화 된 것과 비교하여 ReintrantLock이 조금 더 복잡하다는 것을 알 수 있습니다. 잠금 해제 작업은 마지막으로 잠금 해제되지 않으면 코드에 예외가 있고 잠금이 해제되지 않으며 JVM에 의해 동기화 된 상태가 해제 될 수 있습니다.
그렇다면 ReintrantLock의 탁월한 특성은 무엇입니까?
1.1.1 재입국
단일 스레드를 반복적으로 입력 할 수 있지만 반복적으로 종료해야합니다.
lock.lock (); lock.lock (); try {i ++; } 마침내 {lock.unlock (); lock.unlock ();}ReintrantLock은 재진입 잠금 장치이므로 잠금 관련 획득 카운터가있는 동일한 잠금 장치를 반복적으로 얻을 수 있습니다. 잠금 장치를 소유 한 스레드가 잠금을 다시 가져 오면 획득 카운터가 1 씩 증가하고 실제 릴리스 (재진입 잠금)를 얻으려면 잠금 장치를 두 번 릴리스해야합니다. 이것은 동기화의 의미를 모방합니다. 스레드가 이미 가지고있는 모니터에 의해 보호 된 동기화 된 블록으로 들어가면 스레드를 계속할 수 있습니다. 스레드가 두 번째 (또는 후속) 동기화 된 블록을 종료하면 잠금이 해제되지 않습니다. 잠금은 스레드가 들어가는 모니터에 의해 보호 된 첫 번째 동기화 된 블록을 종료 할 때만 해제됩니다.
공공 계급 아동이 아버지를 확장시킵니다. }} public synchronized void dosomething () {system.out.println ( "1child.dosomething ()"); doanotherthing (); // 자신의 클래스에서 다른 동기화 된 메소드를 호출} 개인 동기화 된 void doanotherthing () {super.dosomething (); // 부모 클래스 시스템의 동기화 된 메소드를 호출합니다. } @override public void run () {child.dosomething (); }} 클래스 아버지 {public synchronized void dosomething () {system.out.println ( "2father.dosomething ()"); }}스레드가 다른 동기화 된 방법으로 들어가서 이전에 얻은 잠금 장치를 해제하지 않음을 알 수 있습니다. 따라서 출력은 여전히 순차적입니다. 따라서 동기화 된 것은 재진입 잠금 장치입니다
산출:
1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
...
1.1.2. 방해 가능
동기화 된 것과 달리 ReintrantLock은 인터럽트에 반응합니다. 인터럽트 관련 지식보기 [높은 동시성 Java 2] 멀티 스레딩 기본 사항
일반적인 lock.lock ()은 인터럽트에 응답 할 수 없으며, 잠금 .lockinterruptibly ()는 인터럽트에 응답 할 수 있습니다.
교착 상태 장면을 시뮬레이션 한 다음 인터럽트를 사용하여 교착 상태를 처리합니다.
패키지 테스트; import java.lang.management.managementFactory; import java.lang.management.threadInfo; import java.lang.management.threadmxbean; import java.util.concurrent.locks.reentrantlock; 공개 정적 재구성 록 1 = 새로운 재구성 (); public static reintrantlock lock2 = new ReintrantLock (); int 잠금; 공개 테스트 (int lock) {this.lock = 잠금; } @override public void run () {try {if (lock == 1) {lock1.lockinterruptible (); try {thread.sleep (500); } catch (예외 e) {// todo : 핸들 예외} lock2.lockinterruptibly (); } else {lock2.lockinterruptibly (); try {thread.sleep (500); } catch (예외 e) {// todo : 핸들 예외} lock1.lockinterruptibly (); }} catch (Exception e) {// todo : handing exception} and manke {if (lock1.isheldbycurrentthread ()) {lock1.unlock (); } if (lock2.isheldByCurrentThread ()) {lock2.unlock (); } system.out.println (Thread.currentThread (). getId () + ": 스레드 종료"); }} public static void main (string [] args)은 중단 된 예를 던졌습니다. {test t1 = new test (1); 테스트 T2 = 새로운 테스트 (2); 스레드 스레드 1 = 새 스레드 (T1); 스레드 스레드 2 = 새 스레드 (T2); thread1.start (); Thread2.start (); Thread.sleep (1000); //deadlockchecker.check (); } 정적 클래스 DeadlockChecker {private final static strandmxbean mbean = managementFactory .getThreadMxBean (); 최종 정적 실행 가능한 Deadlockchecker = new Runnable () {@override public void run () {// todo 자동 생성 메소드 스터브 (true) {long [] DeadlockedThreadIds = mbean.findDeadLockedThreads (); if (deadlockedThreadIds! = null) {ThreadInfo [] ThreadInfos = mbean.getThreadInfo (DeadlockedThreadIds); for (Thread T : ordre.getallStackTraces (). keyset ()) {for (int i = 0; i <ThreadInfos.length; i ++) {if (t.getId () == ThreadInfos [i] .getThreadId ()) {t.interrupt (); }}}}} try {thread.sleep (5000); } catch (예외 e) {// todo : 핸들 예외}}}}; public static void check () {스레드 t = 새 스레드 (Deadlockchecker); T. 세트 데몬 (true); t.start (); }}}위의 코드는 교착 상태를 일으킬 수 있고, 스레드 1은 lock1을 가져오고, 스레드 2가 lock2를 얻은 다음 서로의 자물쇠를 얻기를 원합니다.
위의 코드를 실행 한 후 상황을보기 위해 Jstack을 사용합니다.
교착 상태가 실제로 발견되었습니다.
Deadlockchecker.check (); 방법은 교착 상태를 감지 한 다음 교착 상태를 방해하는 데 사용됩니다. 중단 후 스레드가 정상적으로 나옵니다.
1.1.3. 시간 제한
타임 아웃이 잠금을 얻을 수 없으면 False가 반환되며 데드 잠금 장치를 형성하기 위해 영구적으로 대기하지 않습니다.
Lock.rylock (긴 타임 아웃, 시간 유닛 단위)을 사용하여 시간이 긴 잠금을 구현하고 매개 변수는 시간과 단위입니다.
시간이 제한 될 수 있음을 설명하기 위해 예를 들어 보겠습니다.
패키지 테스트; import java.util.concurrent.timeUnit; import java.util.concurrent.locks.reentrantlock; public class test emplements runnable {public static reentlock lock = new ReentrantLock (); @override public void run () {try {if (lock.trylock (5, timeUnit.seconds)) {Thread.Sleep (6000); } else {System.out.println ( "잠금 실패"); }} catch (예외 e) {} 마지막으로 {if (lock.isheldbycurrentthread ()) {lock.unlock (); }}} public static void main (String [] args) {test t = new test (); 스레드 t1 = 새 스레드 (t); 스레드 t2 = 새 스레드 (t); t1.start (); t2.start (); }}두 개의 스레드를 사용하여 잠금을 위해 경쟁하십시오. 스레드가 잠금을 획득하면 6 초 동안 잠을 자고 각 스레드는 5 초 동안 잠금을 얻으려고합니다.
따라서 잠금을 얻을 수없는 스레드가 있어야합니다. 당신이 그것을 얻을 수 없다면, 당신은 직접 종료됩니다.
산출:
잠금이 실패합니다
1.1.4. 공정한 자물쇠
사용 방법 :
공개 재진입 락 (부울 박람회)
공개 정적 재진입 락 페어 록 = 새로운 재진입 락 (True);
일반적으로 자물쇠는 불공평합니다. 먼저 오는 스레드가 먼저 잠금을 얻을 수있는 것은 아니지만 나중에 오는 스레드는 나중에 잠금을 얻게됩니다. 불공정 한 자물쇠는 기아를 유발할 수 있습니다.
공정한 잠금은이 잠금이 스레드가 먼저 와서 잠금을 먼저 얻을 수 있음을 의미합니다. 공정한 자물쇠는 굶주림을 유발하지는 않지만, 공정한 자물쇠의 성능은 비 공석 자물쇠보다 훨씬 나빠질 것입니다.
1.2 조건
조건과 재진입 락의 관계는 동기화 된 및 Object.Wait ()/Signal ()과 유사합니다.
Await () 메소드는 현재 스레드가 대기하고 현재 잠금 장치를 해제하게합니다. 신호 ()가 다른 스레드 또는 SignalLall () 메소드에서 사용되면 스레드가 잠금을 되찾고 계속 실행됩니다. 또는 실이 중단되면 대기 중에서 뛰어 내릴 수도 있습니다. 이것은 Object.wait () 메소드와 매우 유사합니다.
awaituninterruptily () 메소드는 기본적으로 Await () 메소드와 동일하지만 프로세스 중에 응답 인터럽트를 기다리지 않습니다. singal () 메소드는 스레드 대기를 깨우는 데 사용됩니다. 상대적인 singalall () 메소드는 대기하는 모든 스레드가 깨어납니다. 이것은 objct.notify () 메소드와 매우 유사합니다.
여기에 자세히 소개하지 않을 것입니다. 설명 할 모범을 보여 드리겠습니다.
패키지 테스트; import java.util.concurrent.locks.condition; import java.util.concurrent.locks.reentrantlock; public class test emplements runnable {public static retrantlock lock = new ReintrantLock (); 공개 정적 조건 조건 = lock.newcondition (); @override public void run () {try {lock.lock (); 조건 .await (); System.out.println ( "스레드가 진행 중"); } catch (예외 e) {e.printstacktrace (); } 마침내 {lock.unlock (); }} public static void main (String [] args)은 InterruptedException {test t = new test (); 스레드 스레드 = 새 스레드 (t); thread.start (); Thread.sleep (2000); lock.lock (); 조건 .signal (); lock.unlock (); }}위의 예는 매우 간단합니다. 스레드를 기다렸다가 메인 스레드가 깨어나게하십시오. 조건 .await ()/신호는 잠금을 얻은 후에 만 사용할 수 있습니다.
1.3. 실험금
자물쇠의 경우 상호 배타적입니다. 그것은 내가 자물쇠를 얻는 한 아무도 그것을 다시 얻을 수 없다는 것을 의미합니다.
세마포어의 경우 여러 스레드가 동시에 임계 섹션을 입력 할 수 있습니다. 공유 잠금 장치로 간주 될 수 있지만 공유 한도는 제한적입니다. 한계가 사용 된 후에는 한계를 얻지 못한 다른 스레드가 여전히 임계 영역 외부로 차단됩니다. 금액이 1 인 경우 잠금과 동일합니다.
예는 다음과 같습니다.
패키지 테스트; import java.util.concurrent.executorservice; import java.util.concurrent.executors; import java.util.concurrent.semaphore; 공개 클래스 테스트는 런닝 가능한 {Final Semaphore = New Semaphore (5); @override public void run () {try {semaphore.acquire (); Thread.sleep (2000); System.out.println (thread.currentthread (). getId () + "done"); } catch (예외 e) {e.printstacktrace (); } 마침내 {semaphore.release (); }} public static void main (String [] args)은 InterruptedException {ExecutOrserVice executorService = executors.newfixedthreadpool (20); 최종 테스트 t = new test (); for (int i = 0; i <20; i ++) {executorService.submit (t); }}}20 개의 스레드가있는 스레드 풀이 있으며 각 스레드는 세마포어 라이센스로 이동합니다. 세마포어에는 5 개의 라이센스 만 있습니다. 실행 후 5 개가 배치로 출력되며 배치는 출력임을 알 수 있습니다.
물론 하나의 스레드는 한 번에 여러 라이센스를 신청할 수도 있습니다.
공개 무효 획득 (int remits)은 중단 된 예고를 던졌습니다
1.4 ReadWritelock
readWritelock은 함수를 구별하는 잠금 장치입니다. 읽기와 쓰기는 두 가지 다른 기능입니다. 읽기 읽기는 상호 배타적이지 않고 읽기 쓰기가 상호 배타적이며 쓰기 쓰기는 상호 배타적입니다.
이 설계는 동시성을 증가시키고 데이터 보안을 보장합니다.
사용 방법 :
개인 정적 ReentrantreadWritelock readWritelock = New ReentrantreadWritelock ();
개인 정적 잠금 readlock = readwritelock.readlock ();
개인 정적 잠금 writelock = readwritelock.writelock ();
자세한 예는 생산자 및 소비자 문제, 독자 및 작가 문제의 Java 구현을 볼 수 있으며 여기에서 확장하지는 않습니다.
1.5 CountdownLatch
카운트 다운 타이머의 일반적인 시나리오는 로켓 출시입니다. 로켓이 출시되기 전에 모든 것이 어리석은지 확인하기 위해 다양한 장비와 기기의 검사가 종종 수행됩니다. 엔진은 모든 검사가 완료된 후에 만 점화 할 수 있습니다. 이 시나리오는 CountdownLatch에 매우 적합합니다. 점화 스레드가 모든 체크 스레드를 실행하기 전에 완료 될 때까지 대기 할 수 있습니다.
사용 방법 :
정적 최종 CountdownLatch End = New CountdownLatch (10);
end.countdown ();
end.await ();
도식 다이어그램 :
간단한 예 :
패키지 테스트; import java.util.concurrent.countdownlatch; import java.util.concurrent.executorservice; import java.util.concurrent.executors; 공개 클래스 테스트 구현 실행 가능 {정적 최종 CountdownLatch countdownlatch = new CountdownLatch (10); 정적 최종 테스트 t = new test (); @override public void run () {try {Thread.sleep (2000); System.out.println ( "완료"); countdownlatch.countdown (); } catch (예외 e) {e.printstacktrace (); }} public static void main (String [] args)은 InterruptedException {ExecutOrService executorService = executors.newfixedThreadPool (10); for (int i = 0; i <10; i ++) {executorService.execute (t); } countdownlatch.await (); System.out.println ( "End"); ExecutorService.shutdown (); }}기본 스레드는 "끝"을 출력하기 전에 10 개의 스레드가 모두 실행될 때까지 기다려야합니다.
1.6 Cyclicbarrier
CountdownLatch와 마찬가지로 일부 스레드가 실행되기 전에 완료되기를 기다리고 있습니다. CountdownLatch와의 차이점은이 카운터를 반복적으로 사용할 수 있다는 것입니다. 예를 들어, 카운터를 10으로 설정했다고 가정합니다. 그런 다음 10 개의 스레드의 첫 번째 배치를 수집 한 후 카운터가 0으로 돌아온 다음 10 개의 스레드의 다음 배치를 수집합니다.
사용 방법 :
공개 사이클리 바리어 (Int Parties, Runnable Barrieraction)
장벽은 카운터가 한 번 계산할 때 시스템이 수행 할 작업입니다.
기다리다()
도식 다이어그램 :
예는 다음과 같습니다.
패키지 테스트; import java.util.concurrent.cyclicbarrier; 공개 클래스 테스트 도구 런닝 가능 {private String Soldier; 개인 최종 사이클리 바리어 사이클리; 공개 시험 (현인 군인, Cyclicbarrier Cyclic) {this.soldier = 군인; this.cyclic = cyclic; } @override public void run () {try {// 모든 병사들이 Cyclic.await () 도착하기를 기다립니다. Dowork (); // 모든 병사들이 자신의 작업을 완료하기를 기다립니다. Cyclic.await (); } catch (예외 e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); }} private void dowork () {// todo 자동 생성 메소드 스터브 트리 {thread.sleep (3000); } catch (예외 e) {// todo : hands exception.out.out.println (Soldier + ": done"); } 공개 정적 클래스 Barrierrun은 실행 가능한 {부울 플래그; int n; public barrierrun (부울 플래그, int n) {super (); this.flag = 플래그; this.n = n; } @override public void run () {if (flag) {system.out.println (n + "작업 완료"); } else {system.out.println (n + "set worlod"); flag = true; }}} public static void main (String [] args) {Final int n = 10; 스레드 [] 스레드 = 새 스레드 [n]; 부울 플래그 = 거짓; Cyclicbarrier Barrier = New Cyclicbarrier (N, New Barrierrun (Flag, N)); System.out.println ( "set"); for (int i = 0; i <n; i ++) {system.out.println (i+"report"); 스레드 [i] = 새로운 스레드 (새로운 테스트 ( "Soldier" + I, Barrier)); 스레드 [i] .start (); }}}인쇄 결과 :
모으다
0 보고서
1 보고서
2 보고서
3 보고서
4 보고서
5 보고서
6 보고서
7 보고서
8 보고서
9 보고서
10 세트 완료 군인 5 : 완료
군인 7 : 완료
군인 8 : 완료
군인 3 : 완료
군인 4 : 완료
군인 1 : 완료
군인 6 : 완료
군인 2 : 완료
군인 0 : 완료
군인 9 : 완료
10 개의 작업이 완료되었습니다
1.7 잠금 지원
스레드 차단 원시를 제공합니다
일시 중단과 유사합니다
Locksupport.park ();
Locksupport.unpark (T1);
삭감과 비교하여 실이 얼어 붙는 것은 쉽지 않습니다.
Locksupport의 아이디어는 세마포어와 다소 유사합니다. 내부 라이센스가 있습니다. 주차 할 때이 라이센스를 빼앗아 파악할 때이 라이센스에 적용됩니다. 따라서 파크가 공원 이전에 있으면 스레드 동결이 발생하지 않습니다.
다음 코드는 [High Concurrency Java 2] 멀티 스레딩 재단의 Spentend 샘플 코드입니다. 중단을 사용할 때 교착 상태가 발생합니다.
패키지 테스트; import java.util.concurrent.locks.locksupport; 공개 클래스 테스트 {정적 객체 u = new Object (); 정적 testsuspendthread t1 = new testsuspendthread ( "t1"); 정적 testsuspendthread t2 = new testsuspendthread ( "t2"); public static class testsuspendthread는 스레드 {public testsuspendthread (문자열 이름) {setName (name); } @override public void run () {synchronized (u) {system.out.println ( "in" + getName ()); //thread.currentThread (). SUSTEND (); Locksupport.park (); }}} public static void main (string [] args)은 InterruptedException {t1.start (); Thread.sleep (100); t2.start (); // t1.resume (); // t2.resume (); Locksupport.unpark (T1); Locksupport.unpark (T2); t1.join (); t2.join (); }}그러나 Locksupport를 사용하면 교착 상태가 발생하지 않습니다.
게다가
Park ()는 인터럽트에 응답 할 수 있지만 예외를 제외하지 않습니다. 인터럽트 응답의 결과는 Park () 함수의 반환이 스레드에서 인터럽트 플래그를 얻을 수 있다는 것입니다.
JDK에는 Park를 사용하는 곳이 많이 있습니다. 물론 Locksupport 구현도 unsafe.park ()를 사용하여 구현됩니다.
공개 정적 무효 공원 () {
insafe.park (false, 0l);
}
1.8 ReentrantLock 구현
ReentrantLock의 구현을 소개하겠습니다. ReintrantLock의 구현은 주로 세 부분으로 구성됩니다.
ReintrantLock의 상위 클래스는 동기 상태를 나타내는 상태 변수를 갖습니다.
/*** 동기화 상태. */ 개인 휘발성 int 상태;
CAS 작업을 통해 잠금을 획득하도록 상태를 설정하십시오. 1으로 설정하면 잠금 홀더가 현재 스레드에 제공됩니다.
Final void lock () {if (CompareAndsetState (0, 1)) setExcclubileDownerThread (Thread.CurrentThread ()); 그렇지 않으면 획득 (1); }잠금이 성공하지 못하면 응용 프로그램이 만들어집니다.
공개 최종 void accure (int arg) {if (! tryacquire (arg) && acquirequeued (addwaiter (node.exclusive), arg)) selfinterrupt (); }먼저, 다른 스레드가 잠금을 해제했을 수 있으므로 적용한 후 TryAcquire를 사용해보십시오.
여전히 자물쇠를 신청하지 않은 경우 웨이터를 추가하십시오. 즉, 대기 대기열에 자신을 추가하는 것을 의미합니다.
개인 노드 AddWaiter (노드 모드) {Node Node = 새 노드 (Thread.CurrentThread (), 모드); // ENQ의 빠른 경로를 시도하십시오. 실패 노드에서 전체 ENQ로 백업하면 Pred = Tail; if (pred! = null) {node.prev = pred; if (compareAndsettail (pred, node)) {pred.next = 노드; 리턴 노드; }} enq (노드); 리턴 노드; }이 기간 동안 자물쇠를 신청하려는 많은 시도가있을 것이며, 여전히 신청할 수 없다면 끊을 것입니다.
Private Final Boolean ParkandCheckinterRup () {Locksupport.park (this); return shread.interrupted (); }마찬가지로, 잠금 장치가 릴리스되고 Unpark에 대해 자세히 설명하지 않으면 여기에서 자세히 설명하지 않습니다.
2. 동시 컨테이너 및 일반적인 소스 코드 분석
2.1 Concurrenthashmap
해시 맵은 스레드 안전 컨테이너가 아니라는 것을 알고 있습니다. 해시 맵 스레드-안전을 만드는 가장 쉬운 방법은 사용하는 것입니다.
collections.synchronizedmap, 해시 맵의 래퍼입니다
public static map m = collections.synchronizedmap (new hashmap ());
마찬가지로 목록의 경우 SET도 비슷한 방법을 제공합니다.
그러나이 방법은 동시성 양이 비교적 작은 경우에만 적합합니다.
SynchronizedMap의 구현을 살펴 보겠습니다
개인 최종지도 <k, v> m; // 맵 백킹 맵 최종 객체 뮤 테스; // 동기화 map (map <k, v> m)을 동기화 할 객체 {if (m == null) 버스트 새 nullpointerexception (); this.m = m; MUTEX = 이것; } synchronizedMap (map <k, v> m, object mutex) {this.m = m; this.mutex = mutex; } public int size () {synchronized (mutex) {return m.size ();}} public boolean isempty () {synchronized (return) {return m.isempty ();}} public boolean은 Key (객체 키) {synchronized (meTex) {return value (key); synchronized (mutex) {return m.containsvalue (value);}} public v get (object key) {synchronized (mutex) {return m.get (key);} public v put (k key, v value) {synchronized (mutex) {return m.put (key, value)} {event) M.remove (key);}} public void putall (map <? extends k,? extends v> map) {synchronized (mutex) {m.putall (map);}} public void clear () {synchronized (mutex) {m.clear ();}}해시 맵을 내부에 랩핑 한 다음 해시 맵의 모든 작업을 동기화했습니다.
각 방법은 동일한 잠금 (MUTEX)을 획득하므로 Put and Remain과 같은 작업이 상호 배타적이므로 동시성의 양을 크게 줄입니다.
ConcurrenTashMap이 어떻게 구현되는지 살펴 보겠습니다
public v put (k key, v value) {세그먼트 <k, v> s; if (value == null) 새 nullpointerexception ()을 던지십시오. int hash = 해시 (키); int j = (Hash >>> segmentshift) & segmentmask; if ((s = (segment <k, v>) unsafe.getObject // 비 전환; Recheck (segments, (j << sshift) + sbase)) == null) // ensuresegment s = ensuresegment (j); return s.put (키, 해시, 값, 거짓); }ConcurrEthashMap 내부에는 큰 해시 맵을 여러 세그먼트 (작은 해시 맵)로 나눈 다음 각 세그먼트의 데이터를 해시하는 세그먼트 세그먼트가 있습니다. 이러한 방식으로, 다른 세그먼트에서 여러 스레드의 해시 작업은 스레드 안전이어야하므로 동일한 세그먼트의 스레드를 동기화하면 잠금의 분리를 깨닫고 동시성을 크게 증가시킵니다.
consurenthashmap.size를 사용할 때는 각 세그먼트의 데이터 합계를 계산해야하므로 더 문제가됩니다. 현재 각 세그먼트에 잠금 장치를 추가 한 다음 데이터 통계를 수행해야합니다. 이것은 잠금을 분리 한 후 작은 단점이지만 크기 방법을 고주파수로 호출해서는 안됩니다.
구현 측면에서, 우리는 동기화 및 잠금을 사용하지 않지만 가능한 한 많은 트리 록을 사용하지 않습니다. 동시에, 우리는 또한 해시 맵 구현에 약간의 최적화를 만들었습니다. 나는 여기서 언급하지 않을 것입니다.
2.2 Blockingqueue
Blockingqueue는 고성능 컨테이너가 아닙니다. 그러나 데이터를 공유하기에 아주 좋은 컨테이너입니다. 생산자와 소비자의 전형적인 구현입니다.
도식 다이어그램 :
자세한 내용은 제작자 및 소비자 문제 및 독자 및 작가 문제의 Java 구현을 확인할 수 있습니다.