공유 식 AQS

공유 식 AQS
차단 식 자 물 쇠 는 같은 시간 에 한 개의 스 레 드 만 실 행 될 수 있 습 니 다.한 스 레 드 가 실 행 된 후에 다음 스 레 드 를 방출 합 니 다.공유 식 은 자 물 쇠 는 공유 할 수 있 고 같은 시간 에 여러 개의 스 레 드 가 실 행 될 수 있 습 니 다.
소스 코드 분석 을 통 해 공유 식 AQS 의 실현
Countdown Latch,Semaphore 는 모두 공유 자물쇠 에 속한다.인터넷 을 바탕 으로 많은 블 로그 들 이 Count Downlatch 를 분석 하기 때문에 저 는 여기 서 Semaphore 를 분석 해 보 겠 습 니 다.
Semaphore 의 기본 사용
public class SemaphoreTest {
        public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(3);
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            semaphore.acquire();//   
                            System.out.println(Thread.currentThread().getName() + " " + new Date() + "     ");
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            semaphore.release();//   
                        }
                    }
                }).start();
            }
        }
    }

위의 코드 는 매우 간단 합 니 다.3 개의 스 레 드 를 수용 할 수 있 는 신 호 량 을 초기 화하 고 10 개의 스 레 드 를 초기 화 하 며 스 레 드 마다 인쇄 를 한 번 씩 실행 합 니 다(1 초 지연).이상 코드 실행 결과:열 려 있 는 자물쇠 에 비해 같은 시간 에 여러 스 레 드 를 동시에 실행 할 수 있 습 니 다.Semaphore 의 내부 클래스 구 조 를 보면 클래스 구조 에서 알 수 있 듯 이 Semaphore 도 공평 한 자물쇠 와 불공평 한 자물쇠 의 실현 을 지원 하고 그 자물쇠 의 실현 역시 Sync 를 통 해 AQS 를 통합 하여 이 루어 진다.
Semaphore 의 acquire 방법
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

acquire sync 는 AQS 를 호출 하 는 acquireShared Interruptibly 방법 으로 이 루어 집 니 다.
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())//           
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)//     ,           ,  0       ,  0      ,0    ,         
        doAcquireSharedInterruptibly(arg);
}

cquire Shared Interruptibly 이름 을 보면 이 방법 은 중 단 된 공유 식 으로 자 물 쇠 를 가 져 올 수 있 음 을 알 수 있 습 니 다.try Acquire Shared 도 빈 방법 으로 하위 클래스 에 남 겨 두 었 을 것 입 니 다.
protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

Semaphore 공평 자물쇠 의 try Acquire Shared 실현
protected int tryAcquireShared(int acquires) {
        for (;;) {//CAS        
            if (hasQueuedPredecessors())//         ,    -1,          
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))//        ,      CAS    ,         
                return remaining;
        }
    }

사용 가능 한 자원 이 없 을 때 doAcquire Shared Interruptibly 방법 을 실행 합 니 다.
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);//             。
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//          
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

이때 의 데이터 구 조 는 다음 과 같다.주로 setHead and Propagate 방법 을 보 는데 이 방법 은 스 레 드 가 깨 어 난 후에 실 행 될 것 이다.
private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())//    (         )              ,              
                doReleaseShared();
        }
    }

이러한 깨 우기 방식 과 자 물 쇠 를 배열 하 는 것 은'공유 모델'과 구별 된다.매번 에 처음부터 하나의 스 레 드 를 깨 우 고 그 다음 에 다음 노드 의 스 레 드 를 깨 우려 고 한다.충분 한 자원 만 있 으 면 스 레 드 가 깨 어 나 고 이렇게 반복 된다.자 물 쇠 를 배열 하 는 것 처럼 한 노드 가 실 행 될 때 까지 기 다 렸 다가 다음 노드 를 깨 워 야 합 니 다.
Semaphore 의 release 방법
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {//        
            doReleaseShared();//      
            return true;
        }
        return false;
    }

공유 잠 금 총화
4.567917.공유 자 물 쇠 는 통일 시간 에 여러 개의 스 레 드 가 실 행 될 수 있 습 니 다
4.567917.자물쇠 의 방출 과정 은 배타 적 자물쇠 에 비해 전파 성 이 있 고 한 노드 가 깨 어 나 면 처음부터 한 번 씩 깨 워 본다

좋은 웹페이지 즐겨찾기