동시 소스 코드 분석 도구와 시나리오의 사용을 CountDownLatch를

Fanger 웨이 코드 또는 다음 스캔의 공공 마이크로 채널 번호를 검색 菜鸟飞呀飞, 당신은 마이크로 채널 대중 번호에 초점을 더 읽을 수 Spring源码分析Java并发编程기사.

마이크로 채널 공개 수

간략한 소개

  • CountDownLatch를가 JUC 패키지에서 제공하는 유틸리티 클래스, 그 역할은 또는 다른 스레드 실행을 위해 대기하는 스레드의 그룹을 완료 한 후 다시 수행 할 수 있도록하는 것입니다. 이름에서 추측 할 수, 그것은 0으로 카운터는 감소가, 잠금이 (래치)가 열릴 때 카운터가 한 번 감소되도록, 뒤쪽으로 계산하는 것입니다, 그리고 마지막으로 문을있는 잠금, 그의 작업을 마무리 한 후 각 스레드를 엽니 다 메인 쓰레드 (또는 대기중인 스레드는) 다음 작업을 수행합니다.
  • 실제로, 우리는 오늘에 관한 사용할 수 있습니다, 이러한 스레드의 실행 순서를 제어 할 필요가있다 조인 () 메소드로 제공되는 Thread 클래스를 사용하는 우리가 선택할 수있는이 시간을 문제를 해결하기 위해 여러 스레드를 사용 할 필요가있을 수 있습니다 CountDownLatch를가 해결도 해결하기 위해 다른 클래스으로 CyclicBarrier JUC 패키지로 사용할 수 있습니다 소개했다.

사용 방법

  • CountDownLatch를이 매개 변수는 잠금 (개방)를 해제 해, CountDownLatch 필요 내림차순 전에 횟수를 제어하는 ​​데 사용됩니다, 그것은 단지 생성자, int 형의 매개 변수를 전달하는 생성자 필요가 있으며, 사용이 매우 간단합니다. CountDownLatch를 또한 다음과 같은 세 가지 방법, 아래 표에서 자세한 정보를 제공합니다.
메소드 이름 방법 역할
보이드 AWAIT () CountDownLatch를 카운터가 0으로 감소하면, 스레드는 해제를 허용이 방법을 차단하는 스레드를 호출하자
부울 AWAIT (긴 시간 초과 TimeUnit와 부) 더 지정된 시간보다, 해, CountDownLatch는 카운터가 0으로 감소하지 않은 경우하자 스레드가이 방법의 초과 근무 차단을 호출 한 후 스레드는 직접 돌아갑니다
보이드 카운트 () CountDownLatch를에게 카운터 값이 0으로 감소 카운터 마이너스 1을 보자, CountDownLatch를 스레드의 차단 해제를 차단하자
  • 간단한 장면, 간단한 소개 CountDownLatch를 사용 아래. 학생으로서, 항상 시험의 다양한 각 시험, 과목 교사 계산에게 총 점수를 표시됩니다있다, 총 점수 순위. 각 과목 교사의 채점 속도가 동일하지 않습니다, 그래서 전체 점수와 총 점수 순위 사람들이 마킹 모든 교사의 완료 때까지 기다릴 필요가 있기 때문에이 과정에서 마킹의 대상은, 같은 시간에 있습니다. 이 시간 우리는 CountDownLatch를이 도구 클래스와 프로그램에서이 시나리오를 시뮬레이션 할 수 있습니다. 각 과목 교사의 득점 률 이후 thread와 각 과목의 교사는 동일하지 않습니다 각 과목의 완료를 표시 교사, 그것은 메이크업 카운터 카운트 다운 CountDownLatch를의 () 메서드를 호출 얼마 동안 잠 임의의 스레드, 그래서 모든 교사 0으로 점수 카운터는 감소를 완료하고 주 스레드에서 것이다 기다리고 때 뺀, 메인 쓰레드의 AWAIT () 메소드의 호출 해, CountDownLatch, 목적은 () 메소드 모두의 완료를 표시하는 교사에 대한 메인 쓰레드의 대기하도록하려면 차단 해제에 다음 순위 등, 총 점수를 추가합니다. 다음 데모 예이다.
public class CountDownLatchDemo {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        List<String> teachers = Arrays.asList("语文老师","数学老师","英语老师","物理老师","化学老师","生物老师");
        Random random = new Random();
        // 创建6个线程,模拟6个科目的老师同时开始阅卷
        List<Thread> threads = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            threads.add(new Thread(()->{
                try {
                    int workTime = random.nextInt(6) + 1;
                    // 让线程睡眠一段时间,模拟老师的阅卷时间
                    Thread.sleep(workTime * 1000l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "阅卷完成");
                // 每位老师阅卷完成后,就让计数器减1
                countDownLatch.countDown();
            },teachers.get(i)));
        }
        for (Thread thread : threads) {
            thread.start();
        }
        // 让主线程等待所有老师阅卷完成后,再开始计算总分,进行排名
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有科目的老师均已阅卷完成");
        System.out.println("开始计算总分,然后排名");
    }
}
复制代码

소스 코드 분석

  • CountDownLatch를의 사용을 이해, 다음 해, CountDownLatch의 작품을 봐주세요.
  • CountDownLatch를 실제로 공유 잠금, AQS를 사용하여 기본 큐 동기를 달성했다. CountDownLatch를 생성자에서 카운터의 초기 값은 AQS의 상태 변수, 즉 값을 지정한다. 동시에 그 카운트 () 메소드를 뺀 상태의 값을 허용 할 때마다 한번 호출 액세스 상태로의 다중 스레드를 허용하며 AWAIT ()를 호출 할 때 방법은 상태 값이 있으면 0인지 여부를이 시점에 판정한다 당신이 0 동기 큐 입력 현재의 thread를 대기하지 않는 경우 0, 반환에게 현재의 thread를 할 수 있습니다.
  • CountDownLatch를이 어셈블리 AQS를 상속 할 필요가 내부 동기화를 정의 할 필요가 있으므로, AQS를 사용하여 구현하기 때문에, 동기화에 내부 클래스 CountDownLatch를 정의 (이러한 행위가 관행 AQS 시리즈 잠금입니다), 동기화 상속 AQS, 그리고 AQS의 tryAcquireShared (), tryReleaseShared () 메소드를 다시 작성합니다.
  • 생성자 사용시 인스턴스화 조립체 동기화 및 동기화 생성자 파라미터가있는 것이다 생성자 CountDownLatch를 CountDownLatch를 만들기 AQS 동기 초기화 상태 변수 값은 생성자의 크기의 값은 CountDownLatch를 전달한다 INT 유형 값은 카운터의 크기를 나타내는 데 사용된다.
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
复制代码
private static final class Sync extends AbstractQueuedSynchronizer {

    Sync(int count) {
        setState(count);
    }
}
复制代码
  • CountDownLatch를 호출 할 때, 즉 sync.releaseShared (1)은 AQS releaseShared () 메소드에 대한 카운트 () 메소드를 호출한다. 다음 ReleaseShared 소스 () 방법이다.
public final boolean releaseShared(int arg) {
    // 尝试释放共享锁
    if (tryReleaseShared(arg)) {
        /**
         * 当释放锁完成后,同步状态state=0,此时说明后面的线程可以获取锁了
         * 如果此时同步队列中有人的等待,就唤醒后面的线程
         * 如果无人等待,就将首节点的waitStatus设置为-3,表示同步状态可以无条件的传播下去,即后面的线程都可以直接获取锁了
         */
        doReleaseShared();
        return true;
    }
    return false;
}
复制代码
  • releaseShared () 메소드에서 먼저 서브 클래스에서 tryReleaseShared () 메소드를 호출,이 동기화의 tryReleaseShared () 메소드 해, CountDownLatch에 내부 클래스를 호출합니다. 소스는 다음과 같다.
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    // 将同步状态进行减一,减一之后,同步状态变为0,就返回true,表示可以唤醒同步队列中正在等待的线程了
    for (;;) {
        int c = getState();
        // 在对state进行减一操作之前,会先判断一下state的值是否为0,如果state已经为0了,此时还有线程来对state进行减1,这个时候是不正常的操作,因此会返回false
        if (c == 0)
            return false;
        int nextc = c-1;
        // 利用CAS操作来设置state的值
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
复制代码
  • 0 절단 후 상태의 값이 0이 아닌 경우이 시간, 대기열 대기중인 스레드 동기화를 깨울 수 있음을 나타냅니다, true를 돌려주는 경우 역할 tryReleaseShared () 메소드는, 마이너스 1의 상태의 값을 수 있도록 반환 거짓. tryReleaseShared () 메소드, 그것은합니다 (releaseShared AQS로 돌아갑니다 때) 방법, tryReleaseShared () 메소드가 반환 false의 경우, 직접 끝에 () 반환 releaseShared, tryReleaseShared () 메소드가 반환 true의 경우, 그럼 doReleaseShared () 메소드를 실행한다. doReleaseShared () 메소드는 AQS 템플릿 정의, 공유 잠금 핸들 로직에 사용되는 방법이다, 그것의 주요 역할은 스레드 동기화 큐 대기를 깨워입니다.
  • 콜의 AWAIT CountDownLatch를 ()는 즉 sync.acquireSharedInterruptibly (1)은 AQS acquireSharedInterruptibly () 메소드를 호출 할 때. 다음 원본 acquireSharedInterruptibly () 방법이다.
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    // 响应中断
    if (Thread.interrupted())
        throw new InterruptedException();
    // tryAcquireShared()方法是尝试获取锁
    // 对于CountDownLatch而言,当state=0时,会返回1,这表示锁被所有的线程都释放了,当state不等于0时,会返回-1,表示还有线程持有锁
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
复制代码
  • acquireSharedInterruptibly () 메소드에서 먼저 전화 내부의 클래스 tryAcquireShared CountDownLatch를 동기화 () 메소드는 서브 tryAcquireShared () 메소드를 호출한다. 논리 tryAcquireShared () 메소드는이 0 인 경우는, 0이 아닌 1을 반환 상태 값이 0인지 여부를 결정하고, 매우 간단 -1. 상태가 제로 일 때, 카운터의 값이 모든 스레드가 작업을 완료 한 것을 나타내는 0으로 감소된다 이때 tryAcquireShared () 메소드가 복귀 한 후, AQS 다시, acquireSharedInterruptibly () 메소드에 이렇게 그것은 차단하지 않습니다 현재 스레드의 끝을 지시합니다. 상태 0이 아닌 경우는 그들의 작업 카운트 () 메소드를 호출하지 않고, 다 수행 할 스레드가없고, 카운터의 값이 0으로 감소되지 않은 표시 이때 그렇게 tryAcquireShared () 메소드 -1을 리턴하고 다시 AQS 행 , acquireSharedInterruptibly () 메소드를 통해 직접 아니지만 다음 doAcquireSharedInterruptibly () 메소드를 수행 할 때. doAcquireSharedInterruptibly () 메소드는 메소드 AQS 템플릿이 방법의 주된 효과는 공유 잠금 관련된 프로세싱 로직은 공유 락 취득이 실패한 경우, 동기화 큐 공원에 실을 수 있도록이된다. 카운터의 값이 0이 아닌 경우 이때, 현재 스레드 호출 기다리고 ()에있어서, 다른 스레드가 0으로 감소 카운터 () 메소드 카운트를 호출 할 때까지, 동기화 큐 공원 입력되며, 오직 현재 후 후류 스레드, 또는 현재의 thread가 중단됩니다.
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
复制代码
  • 비슷한 논리와 await를 () 메소드 await를 (긴 시간 초과 예외 : InterruptedException 장치) 메소드를 구현하지만, 시간 제한 판단 기준으로 await를 () 메서드를 추가, 관심 친구가 스스로 공부하실 수 있습니다. 전체적으로 말하기 CountDownLatch를 원리는 상대적으로 간단하고,이를 달성하기 위해 공유 잠금 원칙이지만 tryAcquireShared () 메소드의 논리 tryReleaseShared () 메소드는 약간 변경.

개요

  • 이 논문은 해, CountDownLatch의 역할은, 그것은 스레드 또는 다른 스레드 실행을 위해 대기하는 스레드의 세트가 유일한 실행 스레드 실행 제어의 순서의 목적을 달성 할 수있다, 완료 할 수 있습니다 설명합니다. A는 다음 총 점수를 계산 득점으로 사건을 순위 것은 사용 CountDownLatch를 보여줍니다. 마지막으로, 하부의 CountDownLatch를 CountDownLatch를 소스 구현의 간단한 분석 AQS의 로크를 공유하는 관련 방법에 의해 달성된다.
  • 도중에 CountDownLatch를 그것을 사용하는 것이 가장 주목해야한다 await(long timeout, TimeUnit unit)자식 스레드 처리 작업이 수행되지 않은 경우, 카운터는 제로의 주요 원인으로 감소되지 않도록하는 것이 카운트 () 메소드를 호출되지 않은 것이기 때문에, 스레드를 차단 스레드가 기다리고 차단 된, 아무것도 할 수 없습니다, AWAIT (긴 시간 초과 예외 : InterruptedException 장치)를 사용하는 것이 좋습니다 . 특정 비즈니스 경우 모든 스레드가 다음은 AWAIT () 메소드를 사용하는 것이 주 스레드를 실행하기 전에 실행해야하지만, 코드의 작업을 처리하는 하위 스레드에서, 그것은 ... 마지막으로 문을 사용하려고 ... 캐치 최고라고 요구 다음 finally 블록 카운트 다운 () 호출, 그것은 자신의 구덩이를 파고 매우 쉬운 것입니다.
  • 내가 그 await를 () 메서드의 사용으로 인해 사용 CountDownLatch를 과정에서 하나가, 그리고 마지막 자식 스레드에서 ... 더 사용 시도 ... 캐치 없다, 비즈니스 로직 처리의 스레드 오류가 있기 때문에 마지막으로, 이 글타래에 선도, 메인 스레드가 차단되어 있으므로,이 기다리고 카운트 다운 () 메소드를 호출하지 않습니다. 이 비정상적인 아이 스레드이기 때문에 더 나쁜 존재, 그 아니 시도 ... 캐치 ... 마지막으로, 이번에는 메인 스레드의 吞掉오류 로그가 인쇄되지 않습니다 어떤 결과 스택 이상 자식 스레드.하지 현상은 서비스가 항상 얻을 때 서비스 시작, 그래서, 그들은 오류 로그를 인쇄하지 않는 경우이 코드가 실행되기 때문이다. 이 문제는, 코드 라인은 오랜 시간 동안 실행 된 시간의 만남에서,이 장소에 대해 생각하기 때문에 결코 심지어 테스트 환경에서만 나타났다. 석사 학위에 대한 자신의 멀티 스레드 관련 지식과 결합은 거의 제로, 원인, 서버가 다시 시작 n 번이 아니라보고를 다시 시작 파룬따파 하오 메이크업을 찾지 못했습니다 오랜 시간을 체크하고, 단지 동료가 마지막으로 발견 요청할 수 있습니다 오류가 발생했습니다. 최종 해결책은 결국 하위 스레드에서 ... 시도 ... 캐치를 사용하고 finally 블록 ()에서 카운트 다운을 호출하는 것입니다. 이 문제의하지만 여전히 아주 자신의 음식 때문에 사실, 서버의 스레드의 스택이 발견 될 수있는 경우 멀티 스레드, 그것을보고 jstack을 명령으로 해결하기 위해 오랜 시간이 걸렸의 충분한 이해가 어디없는 문제. 내가 관련 소스 코드에 의해 복잡하게 배울 수있는 시작하기로 결정 그래서,이 때문에 교훈이기도하다.

추천

마이크로 채널 공개 수

추천

출처juejin.im/post/5dcacebe518825574937df84