Semaphore는 JUC 패키지에서 일반적으로 사용되는 클래스입니다. AQS 공유 모드의 응용 프로그램입니다. 여러 스레드가 공유 리소스에서 동시에 작동 할 수 있으며 동시성 수를 효과적으로 제어 할 수 있습니다. 좋은 교통 통제를 달성 할 수 있습니다. Semaphore는 라이센스 개념을 제공하며, 이는 버스 티켓으로 간주 될 수 있습니다. 티켓을 성공적으로 얻는 사람들 만 버스에 올 수 있습니다. 일정 수의 티켓이 있으며 제한없이 발행하는 것은 불가능하여 버스의 과부하로 이어질 것입니다. 따라서 티켓이 발행되면 (버스가 가득 찼을 때) 다른 열차 만 기다릴 수 있습니다. 누군가가 버스에서 반쯤 내리면 그의 위치는 무료가 될 것이므로 다른 사람들이 버스를 타기를 원한다면 다시 티켓을받을 수 있습니다. 세마포어를 사용하여 다양한 풀을 구현할 수 있습니다. 이 기사의 끝에서 간단한 데이터베이스 연결 풀을 작성합니다. 먼저 세마포어의 생성자를 살펴 보겠습니다.
// 생성자 1public semaphore (int remits) {sync = new nonfairsync (허가);} // 생성자 2public semapore (int remits, boolean fair) {sync = fair? New FairSync (허가) : New Nonfairsync (허가);}세마포어는 두 개의 매개 변수가없는 생성자를 제공하지만 매개 변수가없는 생성자는 제공되지 않습니다. 두 생성자 모두 초기 라이센스 수로 전달해야합니다. 생성자 1을 사용하여 구성된 세마포어는 라이센스를 얻을 때 비 공적 방식으로 얻습니다. 생성자 2를 사용하면 매개 변수를 통해 라이센스를 얻는 방법 (공정 또는 불공정)을 지정할 수 있습니다. 세마포어는 주로 외부 세계에 두 가지 유형의 API를 제공하여 라이센스를 얻고 라이센스를 공개합니다. 기본값은 하나의 라이센스를 얻고 해제하는 것이며, 매개 변수를 전달하여 동시에 여러 라이센스를 얻고 해제 할 수도 있습니다. 이 기사에서는 매번 하나의 라이센스를 획득하고 공개하는 상황에 대해서만 이야기 할 것입니다.
1. 라이센스를 얻으십시오
// 라이센스 받기 (응답 인터럽트) 공개 void arcire () throws interruptedException {sync.acquiresharedInterpruptility (1);} // 라이센스 받기 (인터럽트에 응답하지 않음) 공개 void arcireUninterruptibly () {sync.AcquireShared (1);} // return a truthAcquire (nonfair arcubition) {volfair arcuition () sync.nonfairtryacquireshared (1)> = 0;} // 라이센스를 얻으려고 시도합니다 (긴 타임 아웃, TimeUnit Unit)는 InterruptedException {return sync.tryAcquiresharedNanos (1, init.tinut (timeout));}위의 API는 세마포어에서 제공하는 기본 라이센스 획득 작업입니다. 한 번에 하나의 라이센스를 얻는 것도 실제 생활에서 일반적인 상황입니다. 직접 가져 오는 것 외에도 가져 오려는 시도도 제공합니다. 직접 페치 작업은 실패 후 스레드를 차단할 수 있으며, 가져 오려고 시도하는 것은 그렇지 않습니다. 또한 TryAcquire 방법은 불공평 한 방식으로 그것을 얻는 데 사용됩니다. 정상 시간에 자주 사용하는 것은 라이센스를 얻는 것입니다. 그것이 어떻게 얻는 지 살펴 보겠습니다. 획득 메소드가 직접 호출 된 획득 방법 (1)을 알 수 있습니다. 이 방법은 AQS의 방법입니다. 우리는 한때 AQS 소스 코드 시리즈 기사에 대해 이야기했습니다. 다시 검토합시다.
// 중단 가능한 모드에서 잠금을 획득 (공유 모드) 공개 최종 void arcredInterUrpruptibly (int arg)는 중단 exception {// 먼저 스레드가 중단되었는지 여부를 결정합니다. 그렇다면 (thread.interrupted ()) {new interruptedException (); } // 1. (tryacquireshared (arg) <0) {// 2. 취득이 실패하면 doacquiresharedinterpruptily (arg) 메소드를 입력하십시오. }}획득 한 첫 번째 방법은 획득 할 수있는 tryacquireshared 방법을 호출하는 것입니다. TreeAcquireshared는 aqs의 추상적 방법입니다. 두 개의 파생 클래스 FairSync와 Nonfairsync는이 방법의 논리를 구현합니다. Fairsync는 공정 획득의 논리를 구현하는 반면, 비 연합 동기화는 비 공석 획득의 논리를 구현합니다.
초록 정적 클래스 동기화 확장 AbstractQueuedSynchronizer {// 최종 int int nonfairtryacquireshared (int acquires) {for (;;) {// 사용 가능한 라이센스 get int = getState (); // 나머지 라이센스를 가져옵니다 int 나머지 = 사용 가능 - 획득; // 1. 0 미만인 경우 직접 남아있는 반환 // 2. 0보다 크게 남아있는 경우 먼저 동기화 상태를 업데이트 한 다음 나머지 IF (<0 || CompareAndSetState (사용 가능, 나머지)) {나머지 반환; }}}} // nonfairsync static final class nonfairsync 확장 sync {private static final long serialversionuid = -26941836836844443567898L; nonfairsync (int remits) {super (허가); } // 라이센스 보호 된 int tryacquireshared (int arcides) {return nonfairtryacquireshared (획득); }} // FAIR SYNCHRONIZER STATIC FINAL CLAST FAIRSYNC 확장 SYNC {private static final long serialversionUID = 2014338818796000944L; fairsync (int remits) {슈퍼 (허가); } // 라이센스를 얻으려고 시도한 int tryacquireshared (int acquires) {for (;;) {// 동기화 대기열 앞에 누군가가 있는지 판단합니다. } // 사용 가능한 라이센스 받기 int int availed = getState (); // 나머지 라이센스를 가져옵니다. // 1. 0 미만인 경우 남은 // 2로 직접 돌아갑니다. 나머지가 0보다 크면, 동기화 상태가 먼저 업데이트 된 다음 나머지 if (<0 || compareAndsetState (availing, waying)) {나머지 반환; }}}}여기서는 비정형 동기화의 TreeAcquireshared 방법이 부모 클래스 동기화에있는 비 에어 트리 콰이어 셰어드 방법을 직접 호출한다는 점에 유의해야합니다. 비 공석 획득 잠금의 논리는 먼저 현재 동기화 상태 (동기 상태가 라이센스 수를 나타내는)를 꺼내고 현재 동기화 상태의 매개 변수를 빼는 것입니다. 결과가 0보다 미치지 않으면 여전히 사용 가능한 라이센스가 있음이 증명되면 CAS 작업을 사용하여 동기화 상태의 값이 직접 업데이트되며, 결과가 0보다 작 든 결과 값이 반환됩니다. 여기서 우리는 TryacquiReshared 방법의 반환 값의 의미를 이해해야합니다. 음수를 반환하면 획득이 실패했으며 0은 현재 스레드가 성공적으로 획득되었지만 후속 스레드는 더 이상 얻을 수 없으며 양수 숫자는 현재 스레드가 성공적으로 획득되고 후속 스레드도 얻을 수 있음을 의미합니다. AcquirSharedInterpruptily 방법의 코드를 살펴 보겠습니다.
// 인터럽트 모드에서 잠금을 획득 (공유 모드) 공개 최종 void arceResharedInterpruptibly (int arg)는 중단 exception {// 먼저 스레드가 중단되었는지 여부를 결정합니다. 그렇다면 (thread.interrupted ()) {new interruptedException (); } // 1. 잠금을 획득하려고 시도하십시오. 취득이 실패하면 doacquiresharedinterpruptily (arg) 메소드를 입력하십시오. }}반환 된 나머지가 0보다 작은 경우 획득이 실패 함을 의미합니다. 따라서, tryacquireshared (arg) <0은 사실이므로 doacquiresharedinterpruptility 방법이 다음에 호출됩니다. 우리가 AQ에 대해 이야기하면 현재 스레드를 노드로 랩핑하여 동기화 큐의 꼬리에 넣고 스레드를 중단 할 수 있습니다. 또한 스레드가 0 미만으로 남아있을 때 스레드가 대기열 위로 올라가고 차단하는 이유입니다. 반환 된 나머지> = 0이라면 현재 스레드가 성공적으로 획득되었음을 의미합니다. 따라서, tryacquireshared (arg) <0은 플라스 지이므로, doacquiresharedinterpruptility 방법은 더 이상 현재 스레드를 차단하기 위해 호출되지 않습니다. 위는 불공정 한 획득의 전체 논리입니다. 공정한 인수는 동기화 대기열에서 누군가가 대기하는지 여부를 결정하기 위해 HasqueuedPredecessors 메소드를 호출하면됩니다. 그렇다면 Return -1은 취득에 실패했음을 직접 나타냅니다. 그렇지 않으면 다음 단계는 불공정 한 획득으로 계속됩니다.
2. 라이센스를 릴리스하십시오
// 라이센스 공개 공개 void release () {Sync.ReleasShared (1);}릴리스 방법을 호출하는 것은 라이센스를 릴리스하는 것입니다. 작동은 매우 간단하므로 AQS의 방출 방법을 호출합니다. 이 방법을 살펴 보겠습니다.
// 릴리스 잠금 작동 (공유 모드) 공개 최종 부울 방출 (int arg) {// 1. if (tryReleasShared (arg)) {// 2. 릴리스가 성공하면 다른 스레드 DoreLeasShared ()를 깨우십시오. 진실을 반환하십시오. } return false;}AQS의 방출 방법은 먼저 TryReleasShared 방법을 호출하여 잠금을 해제하려고 시도합니다. 이 방법의 구현 로직은 서브 클래스 동기화에 있습니다.
초록 정적 클래스 동기화 확장 AbstractQueuedSynchronizer {... // 작업을 해제하려고 시도합니다. 보호 된 최종 부울 tryReleasShared (int 릴리스) {for (;;) {// 현재 동기화 상태를 가져옵니다 int current = getState (); // int next = current + 릴리스; // 추가 결과가 현재 동기화 상태보다 낮 으면 (Next <current) {Throw New Error ( "최대 허가 카운트를 초과") 인 경우 오류가보고됩니다. } // CAS 모드에서 동기화 상태의 값을 업데이트하고 업데이트가 성공한 경우 TRUE를 반환하십시오. 그렇지 않으면 (CompareAndSetState (현재, Next)) {return true; }}} ...}TryReleasShared 방법은 for loop to spin을 사용한다는 것을 알 수 있습니다. 먼저 동기화 상태를 가져오고 들어오는 매개 변수를 추가 한 다음 CAS에서 동기화 상태를 업데이트하십시오. 업데이트가 성공하면 true를 반환하고 메소드에서 벗어나십시오. 그렇지 않으면 루프가 성공할 때까지 계속됩니다. 이것은 세마포어가 라이센스를 공개하는 과정입니다.
3. 연결 풀을 수동으로 쓰십시오
세마포어 코드는 그리 복잡하지 않습니다. 일반적으로 사용되는 작업은 라이센스를 얻고 해제하는 것입니다. 이러한 작업의 구현 로직은 비교적 간단하지만 세마포어의 광범위한 적용을 방해하지는 않습니다. 다음으로 세마포어를 사용하여 간단한 데이터베이스 연결 풀을 구현합니다. 이 예를 통해 독자들이 세마포어의 사용에 대해 더 깊이 이해할 수 있기를 바랍니다.
공개 클래스 ConnectPool {// 연결 풀 크기 개인 int 크기; // 데이터베이스 연결 수집 개인 Connect [] Connects; // 연결 상태 플래그 프라이빗 부울 [] ConnectFlag; // 나머지 수의 연결된 연결 개인 휘발성 int 사용 가능; // 세마포어 개인 세마포어 세마포어; // 생성자 public connectPool (int size) {this.size = size; this.available = size; 세마포어 = 새로운 세마포어 (크기, 참); Connects = New Connect [size]; ConnectFlag = 새로운 부울 [크기]; initConnects (); } // 연결 초기화 private void initConnects () {// 지정된 데이터베이스 연결 수를 생성합니다 (int i = 0; i <this.size; i ++) {connects [i] = new connect (); }} // 데이터베이스 연결 가져 오기 개인 연결 개인 동기화 된 Connect getConnect () {(int i = 0; i <connectflag.length; i ++) {// 컬렉션을 전송하여 사용되지 않은 연결을 찾으려면 (! connectflag [i]) {// in connectflag [i] = true; // 사용 가능한 연결 수를 빼고; System.out.println ( "【"+thread.currentThread (). getName ()+"】 남은 연결 수를 얻으려면 :"+사용 가능); // 연결 참조 리턴 리턴 리턴 연결 [i]; }} return null; } // Connection 가져 오기 공개 Connect OpenConnect ()가 잠복 됨 InterruptedException {// 라이센스 가져옵니다 semaphore.acquire (); // 데이터베이스 연결 가져옵니다. return return getConnect (); } // 연결 릴리스 공개 공개 동기화 된 void release (connect connect) {for (int i = 0; i <this.size; i ++) {if (connect == connects [i]) {// 사용되지 않은 연결을 설정 [i] = false; // 사용 가능한 연결 번호 1을 추가합니다. system.out.println ( "【"+thread.currentThread (). getName ()+"] 나머지 연결 번호를 릴리스하려면"+사용 가능); // 라이센스 릴리스 세마 파어. release (); }}} // 사용 가능한 연결 번호 추가 공개 int availed () {return availity; }}테스트 코드 :
public class testthread는 스레드 {private static connectpool pool = new ConnectPool (3); @override public void run () {try {connect connect = pool.openConnect (); Thread.sleep (100); // 휴식 풀을 가져갑니다. Release (Connect); } catch (InterruptedException e) {e.printstacktrace (); }} public static void main (string [] args) {for (int i = 0; i <10; i ++) {new TestThread (). start (); }}}테스트 결과 :
우리는 배열을 사용하여 데이터베이스 연결에 참조를 저장합니다. 연결 풀을 초기화 할 때는 InitConnects 메소드를 호출하여 지정된 수의 데이터베이스 연결을 작성하고 배열에 참조를 저장합니다. 또한 연결을 사용할 수 있는지 여부를 기록 할 수있는 동일한 크기의 배열이 있습니다. 외부 스레드가 연결을 얻도록 요청할 때마다 먼저 Semaphore.acquire () 메소드를 호출하여 라이센스를 얻은 다음 연결 상태를 사용중인 상태로 설정 한 다음 마지막으로 연결 상태를 반환하십시오. 라이센스 수는 건설 중에 전달 된 매개 변수에 의해 결정됩니다. Semaphore.acquire () 메소드가 호출 될 때마다 라이센스 수가 1 씩 줄어 듭니다. 숫자를 0으로 줄이면 연결할 수없는 연결이 없음을 의미합니다. 현재 다른 스레드가 다시 얻으면 차단됩니다. 스레드가 연결을 공개 할 때마다 Semaphore.Release ()가 호출되어 라이센스를 해제합니다. 현재 총 라이센스 수가 다시 증가하므로 이용 가능한 연결 수가 증가했습니다. 이전에 차단 된 스레드가 깨어나 연결을 계속 얻습니다. 현재 연결을 다시 얻어 연결을 성공적으로 얻을 수 있습니다. 테스트 예에서는 3 개의 연결의 연결 풀이 초기화됩니다. 테스트 결과에서 스레드가 연결을 얻을 때마다 나머지 연결 수가 1만큼 줄어 듭니다. 스레드가 0으로 감소하면 다른 스레드는 더 이상 얻을 수 없습니다. 이 시점에서는 연결을 계속 얻기 전에 스레드가 연결을 해제 할 때까지 기다려야합니다. 나머지 연결의 수는 항상 0과 3 사이에서 변경됨을 알 수 있습니다. 즉, 테스트가 성공적이었습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.