6. AQS 및 동기 화 구성 요소
20940 단어 자바 병렬 프로 그래 밍 실전
AQS 는 JUC 의 핵심 이다.JUC 는 JAVA 의 병발 성능 을 크게 향상 시 켰 다.
그 밑 에 있 는 것 은 양 방향 목록 을 사용 하 는 것 이 고 대열 의 실현 이기 때문에 이것 을 하나의 대열 로 간주 할 수도 있다. 그 중에서 Sync queue 는 동기 화 목록 이 고 양 방향 목록 으로 head, tail 노드 를 포함한다. 그 중에서 head 노드 는 주로 후속 적 인 스케줄 링 에 사용 된다.Condition queue 는 단 방향 링크 입 니 다. 필요 한 것 이 아 닙 니 다. 프로그램 에 Condition 이 필요 할 때 만 이 단 방향 링크 가 존재 하고 여러 Condition queue 가 있 을 수 있 습 니 다.
AQS 가 실현 하 는 대체적인 사고방식: 먼저 AQS 내부 에서 CLH 대기 열 을 유지 하여 자 물 쇠 를 관리 합 니 다. 스 레 드 는 먼저 자 물 쇠 를 가 져 오 려 고 시도 합 니 다. 실패 하면 현재 스 레 드 와 대기 상태 등 정 보 를 NODE 노드 로 묶 어서 동기 화 대기 열 (Sync queue) 에 추가 합 니 다. 그 다음 에 계속 순환 을 따라 자 물 쇠 를 가 져 오 려 고 시도 합 니 다. 그 조건 은 현재 노드 가 head 인 직접적인 후계 가 되 어야 시도 할 수 있 습 니 다.실패 하면 자신 이 깨 어 날 때 까지 자신 을 막 고 자 물 쇠 를 가 진 스 레 드 가 자 물 쇠 를 풀 때 대기 열 에 있 는 후계 스 레 드 를 깨 웁 니 다.
2. AQS 동기 화 구성 요소
2.1 CountDownLatch
계수 기 는 0 으로 줄 여야 기다 리 는 스 레 드 에서 계속 실 행 됩 니 다.한 번 만 사용 할 수 있 고 리 셋 할 수 없습니다.
예 를 들 어 연 산 량 이 매우 많은 임무 가 있 는데 우 리 는 그것 을 여러 개의 하위 임무 로 나 누 어 모든 하위 임 무 를 완성 한 후에 마지막 총 작업 을 집행 할 수 있다.
@Slf4j
public class CountdownLatchTest {
private static int threadCount = 200;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for(int i=0;i(() -> {
try{
test(threadNum);
}catch (Exception e){
log.error("exection",e);
}finally {
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("finish ...");
exec.shutdown();
}
private static void test(int threadNum) {
log.info("{}",threadNum);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
실행 결과 마지막 점 을 캡 처 했 습 니 다:
16:34:36.385 [pool-1-thread-90] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 89
16:34:36.385 [pool-1-thread-84] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 83
16:34:36.385 [pool-1-thread-92] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 91
16:34:36.385 [pool-1-thread-94] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 93
16:34:36.385 [pool-1-thread-96] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 95
16:34:36.385 [pool-1-thread-98] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 97
16:34:36.386 [pool-1-thread-100] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - 99
16:34:36.516 [main] INFO com.njupt.swg.threadstudyjimin.item07.CountdownLatchTest - finish ...
그렇다면 이런 장면 이 라면 몇 개의 키 임 무 를 계산 하고 시간 을 정 해 이 시간 을 초과 하면 이 임 무 를 포기 하 라.
countDownLatch.await(10, TimeUnit.MILLISECONDS);
2.2 Semaphore
같은 시간 에 스 레 드 의 수 를 조절 할 수 있 습 니 다.
Semaphore 가 실현 하 는 기능 은 화장실 에 5 개의 구덩이 가 있 는 것 과 유사 하 다. 만약 10 명 이 화장실 에 가 려 고 한다 면 동시에 몇 명 만 화장실 에 갈 수 있 을 까?동시에 5 명 만 점용 할 수 있 고 5 명 중 한 명 이 비 키 면 그 중 기다 리 는 다른 5 명 중 한 명 이 점용 할 수 있다.또한 기다 리 는 5 명 중 무 작위 로 우선 기 회 를 얻 을 수도 있 고 선착순 으로 기 회 를 얻 을 수도 있 습 니 다. 이 는 Semaphore 대상 을 구성 할 때 들 어 오 는 매개 변수 옵션 에 달 려 있 습 니 다.단일 신 호 량 의 Semaphore 대상 은 상호 배척 자물쇠 의 기능 을 실현 할 수 있 고 한 라인 에서 '자물쇠' 를 얻 을 수 있 으 며 다른 라인 에서 '자물쇠' 를 방출 할 수 있다. 이것 은 자물쇠 복구 의 일부 장소 에 응용 할 수 있다.
@Slf4j
public class SemaphoreLatchTest {
private static int threadCount = 20;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for(int i=0;ifinal int threadNum = i;
exec.execute(() -> {
try{
semaphore.acquire();//
test(threadNum);
semaphore.release();//
}catch (Exception e){
log.error("exection",e);
}
});
}
log.info("finish ...");
exec.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
Thread.sleep(1000);
log.info("{}",threadNum);
}
}
실행 결 과 는 1 초 에 세 개의 스 레 드 를 실행 합 니 다.여 기 는 하나의 스 레 드 로 허 가 를 받 으 면 같은 시간 에 세 개의 스 레 드 가 들 어 와 함께 일 할 수 있 습 니 다.그럼 제 가 하나의 스 레 드 로 바 꿔 서 세 가지 허 가 를 받 으 면 요?한 사람 이 세 개의 구 덩이 를 동시에 차지 하 는 것 처럼 이 사람 이 다 당 겨 야 다음 사람 차례 가 올 수 있다. 그러면 이 때 는 하나의 스 레 드 와 같다.
semaphore.acquire(3);
test(threadNum);
semaphore.release(3);
이 장면 을 고려 해 보 자. 병발 이 너무 높 아서 라인 의 수량 을 조절 하 더 라 도 비교적 까다롭다.화장실 하나 에 구덩이 가 세 개 있 고 밖 에 사람 이 너무 많아 서 세 사람 을 들 어 오 게 하고 다른 사람 은 모두 쫓 아 냈 다.어떻게 할 까요?
if(semaphore.tryAcquire()){//
test(threadNum);
semaphore.release();
}
출력 결과: 세 개의 정보 만 인쇄 되 고 다른 스 레 드 는 모두 버 려 집 니 다.
시간 초과 도 줄 수 있 고 여 기 는 5000 밀리초 입 니 다.명령 마다 1000 밀리초 를 실행 해 야 합 니 다. 그러면 프로그램 은 1000 밀리초 후에 세 개 를 인쇄 합 니 다.그리고 1000 밀리초 만 더 기다 리 면 새로운 세 가지 허 가 를 받 고 세 가 지 를 인쇄 할 수 있 습 니 다.5000 밀리초 다 쓸 때 까지3 * 5 개의 기록 을 인쇄 할 수 있 습 니 다.나머지 5 개 기록 은 시간 초과 로 모두 포기 됐다.
화장실 에 가 는 예: 모두 세 개의 구덩이 입 니 다. 저 는 한 시간 을 정 했 습 니 다. 먼저 세 사람 이 들 어 왔 습 니 다. 자, 그들 은 모두 10 분 안에 해결 합 니 다. (여기 서 마지막 사람 이 딱 10 분 이 걸 렸 다 고 가정 합 니 다) 그러면 이 세 개의 구 덩이 는 계속 사용 할 수 있 습 니 다. 다음 세 사람 이 들 어 와 서 사용 하고 가설 은 마침 10 분 이 걸 렸 습 니 다.그럼 한 시간 까지 몇 명 이 화장실 에 갈 수 있 습 니까?
if(semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)){
test(threadNum);
semaphore.release();
}
2.3 CyclicBarrier
Cyclic Barrier 도 동기 화 보조 클래스 입 니 다. 하나의 스 레 드 가 서로 기다 릴 수 있 습 니 다. 특정한 공공 장벽 에 도착 할 때 까지 여러 스 레 드 간 에 서로 기다 릴 수 있 습 니 다. 모든 스 레 드 가 준 비 된 후에 야 각자 후속 작업 을 계속 할 수 있 습 니 다. Count Downlatch 와 비슷 한 곳 은 바로..이것 도 계수 기 를 통 해 이 루어 집 니 다. 어떤 스 레 드 가 await () 방법 을 호출 한 후에 이 스 레 드 는 대기 상태 에 들 어 갔습니다. 그리고 계수 기 는 - 1 작업 을 합 니 다. 계수기 의 값 이 우리 가 설정 한 초기 값 0 에 이 르 렀 을 때 이전에 await () 를 호출 했 습 니 다.방법 은 대기 상태 에 들 어간 스 레 드 가 깨 어 나 후속 작업 을 계속 수행 합 니 다. Cyclicbarrier 가 스 레 드 를 방출 한 후에 다시 사용 할 수 있 기 때문에 순환 장벽 이 라 고도 합 니 다. Cyclicbarrier 의 사용 장면 은 CountDownlatch 와 비슷 하여 다 중 스 레 드 로 데 이 터 를 계산 하고 결 과 를 합 친 응용 장면 입 니 다.
둘 의 차이:
@Slf4j
public class CyclicBarrierTest {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<6;i++){
Thread.sleep(1000 );
exec.execute(() -> {
try{
race();
}catch (Exception e){
log.error("exception",e);
}
});
}
exec.shutdown();
}
private static void race() throws InterruptedException {
Thread.sleep(1000);
log.info("ready...");
try {
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
log.info("continue...");
}
}
실행 효과: 먼저 레이스 방법 을 1 초 간격 으로 실행 하여 ready 를 출력 합 니 다. 3 개의 스 레 드 인쇄 가 끝나 면 바로 막 힌 log. info ("contine...");모두 인쇄 하 다.
[pool-1-thread-1] INFO ready...
[pool-1-thread-2] INFO ready...
[pool-1-thread-3] INFO ready...
[pool-1-thread-1] INFO continue...
[pool-1-thread-2] INFO continue...
[pool-1-thread-3] INFO continue...
[pool-1-thread-4] INFO ready...
[pool-1-thread-2] INFO ready...
[pool-1-thread-3] INFO ready...
[pool-1-thread-4] INFO continue...
[pool-1-thread-2] INFO continue...
[pool-1-thread-3] INFO continue...
시간 초과 설정 도 할 수 있 고 시간 초과 하면 기다 리 지 않 는 다.
private static void race() throws InterruptedException {
Thread.sleep(1000);
log.info("ready...");
try {
cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.warn("exception",e);
}
log.info("continue...");
}
모두 가 준비 가 다 되 었 을 때, 먼저 한 가지 일 을 한다 면, Cyclic Barrier 를 설명 한 후에 스 레 드 를 추가 해서 실행 할 수 있 습 니 다.
마치 회의 가 있 는 것 처럼 사람들 이 모두 도착 한 후에 우 리 는 소 리 를 질 렀 다. 사람들 이 모두 도착 했다. 우 리 는 지금 회 의 를 시작 했다.본 격 적 인 회 의 를 시작 하 겠 습 니 다.
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,() -> {
log.info("callback is running...");
});
3. ReentrantLock 과 자물쇠
3.1 ReentrantLock 과 synchronized 의 차이
3.2 ReentrantLock 의 장점
이 가운데 ReentrantReadWriteLock 은 읽 기와 쓰기 자물쇠 가 없 는 상태 에서 만 기록 자 물 쇠 를 가 질 수 있다.많이 읽 고 적 게 쓰 면 쓰기 가 배 고 플 수 있다.