자바 다 중 스 레 드 잠 금 및 Condition 류 사용 사례 분석

이 글 은 자바 다 중 스 레 드 잠 금 및 Condition 류 의 사용 사례 분석 을 소개 합 니 다.이 글 은 예제 코드 를 통 해 매우 상세 하 게 소개 되 어 있 으 며,여러분 의 학습 이나 업무 에 대해 어느 정도 참고 학습 가 치 를 가지 고 있 으 며,필요 한 친 구 는 참고 하 실 수 있 습 니 다.
인터넷 에서 매우 많은 운행 코드 를 보 았 습 니 다.많은 것 이 중복 되 었 습 니 다.한 가지 더 말씀 드 리 겠 습 니 다.자바 늙 은 새 에 게 자바 의 다 중 스 레 드 를 이해 하 는 것 은 매우 쉬 운 일이 지만 저 같은 풋내기 에 게 이것 은 정말 어렵 습 니 다.제 가 너무 못 했 나 봐 요.인터넷 에서 반복 되 는 진술 은 내 가 이 문 제 를 이해 하 는 데 아무런 도움 이 되 지 않 는 다.그래서 여기 서 나 는 이 문제 에 대한 이 해 를 적 었 다.그 목적 은 내 가 잊 지 않도록 하기 위해 서 이다.
아니면 코드 인 스 턴 스 부터 말 할 까요?
코드

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;

public class Main {
  public static void main(String[] args) throws InterruptedException {
    MyBlockingQueue<Integer> queue = new MyBlockingQueue<>(1);
    for (int i = 0; i < 10; i++) {
      int data = i;
      new Thread(() -> {
        try {
          queue.enqueue(data);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }).start();
    }
    System.out.println("1111111");
    for(int i=0;i<10;i++){
      new Thread(() -> {
        try {
          queue.dequeue();
        }catch (InterruptedException e){
          e.printStackTrace();
        }
      }).start();
    }
  }
  public static class MyBlockingQueue<E> {
    int size;//        
    ReentrantLock lock = new ReentrantLock(true);
    LinkedList<E> list=new LinkedList<>();//      
    Condition notFull = lock.newCondition();//         
    Condition notEmpty = lock.newCondition();//         
    public MyBlockingQueue(int size) {
      this.size = size;
    }
    public void enqueue(E e) throws InterruptedException {
      lock.lock();
      try {
        while(list.size() ==size)//    , notFull     
          notFull.await();

        list.add(e);//  :      
        System.out.println("  :" +e);
        notEmpty.signal(); //   notEmpty        
      } finally {
        lock.unlock();
      }
    }
    public E dequeue() throws InterruptedException {
      E e;
      lock.lock();
      try {
        while(list.size() == 0)
          notEmpty.await();
        e = list.removeFirst();//  :       
        System.out.println("  :"+e);
        notFull.signal();//   notFull        
        return e;
      } finally {
        lock.unlock();
      }
    }
  }
}
주 함수 가 20 개의 스 레 드 를 시 작 했 습 니 다.앞의 10 개 는 입대 한 후 10 개 는 팀 에서 나 왔 습 니 다.우 리 는 출력 결 과 를 볼 수 있 습 니 다.

new Thread(() -> {
try {
queue.enqueue(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start(); 
스 레 드 구현 에 주의 하 십시오.이것 은 lambda 표현 식 이 Runable 인 터 페 이 스 를 실현 하 는 것 입 니 다.

  :0
  :0
  :2
  :2
  :1
  :1
  :3
  :3
  :4
  :4
  :5
  :5
  :6
  :6
  :7
  :7
  :8
  :8
  :9
  :9
1111111 이 첫 번 째 로 팀 을 나 가기 전에 대열 의 용량 이 1 인 것 을 볼 수 있다.즉,첫 10 개의 입단 과정 은 첫 번 째 성공 만 있 고 다른 것 은 모두 막 혔 다.
그리고 팀 에 들 어 가 는 순 서 는 순환 순서에 따라 자 물 쇠 를 요청 순서에 따라 얻 는 것 을 설명 합 니 다.선착순 입 니 다.이것 은 바로 공평 자물쇠 라 는 뜻 입 니 다.사실 ReentrantLock 은 공평 자물쇠 일 수도 있 고 불공평 자물쇠 일 수도 있 습 니 다.초기 화 할 때 구조 편지 에 true 를 넣 으 면 공평 자물쇠 이 고 false 는 불공평 자물쇠 입 니 다.
재 입 자물쇠 가 무엇 인지 에 대해 서 는 이 편 을 볼 수 있 습 니 다https://www.jb51.net/article/175192.htm이것 은 재 입 자물쇠 의 인 스 턴 스 입 니 다.
그 중 한 줄 의 코드 가 이상 하 다.lock.lock();저 같은 몽 신 에 게 이 코드 가 무슨 일이 일 어 났 는 지 궁금 합 니 다.인터넷 에서 자 물 쇠 를 얻 었 다 고 하 는 경우 가 많 지만'얻 었 다'는 것 은 구체 적 이지 않 습 니 다.그래서 제 가 상상 해 봤 는데 대체적으로 이런 그림 인 것 같 습 니 다.

제 얕 은 경험 을 통 해 알 수 있 듯 이 jvm 은 하나의 준비 대기 열 만 있 습 니 다.준비 대기 열 안의 스 레 드 는 대기 열 순서에 따라 cpu 자원 을 사용 합 니 다.잠 금 을 넣 지 않 으 면 모든 스 레 드 는 순서대로 자원 을 얻 을 수 있 습 니 다.그러나 대상 을 대상 으로 하기 때문에 준비 대기 열 안의 스 레 드 의 입 대 순 서 를 직접 제어 할 수 없습니다.이 럴 때 는 스 레 드 의 운행 순 서 를 제어 하여 처리 논리 가 정확 하도록 해 야 한다.
코드 의 lock()방법 을 결합 하여 cpu 에 스 레 드 가 들 어 오고 lock()을 호출 합 니 다.이 잠 금 이 다른 스 레 드 에 의 해 가 져 오지 않 았 다 면 이 스 레 드 는 cpu 시간 을 사용 할 수 있 습 니 다.이 잠 금 이 다른 스 레 드 에 의 해 가 져 왔 다 면 이 스 레 드 는 차단 되 고 차단 대기 열 에 들 어 갈 것 입 니 다.이렇게 말 하면'가 져 오기'라 는 단어 도 이해 하기 어 려 운 것 이 없습니다.사실은 표시 일 뿐 입 니 다.그리고 lock()방법 은 현재 스 레 드 가 cpu 시간 을 사용 하 는 지,아니면 차단 대기 열 에 들 어 가 는 지 판단 하 는 것 일 뿐 입 니 다.
unlock()방법 을 보고 위의 그림 에서 도움 을 주 었 습 니 다.사실 이것 도 이해 하기 쉽 습 니 다.사실은 대기 열 을 막 는 팀 의 첫 번 째 스 레 드 를 팀 에서 내 고 준 비 된 대기 열 에 들 어 가 는 것 입 니 다.
실행 과정 에서 여러 개의 잠 금 인 스 턴 스 가 있 으 면 몇 개의 차단 가능 한 스 레 드 가 있 을 지 추측 할 수 있 습 니 다.그러면 여러 개의 잠 금 을 사용 하 는 것 외 에 다른 방법 으로 차단 스 레 드 를 증가 하 는 것 이 있 습 니 다.Condition 류 를 사용 하 는 것 입 니 다.condition 류 의 await()방법 을 지적 하면 현재 스 레 드 를 막 은 다음 에 현재 스 레 드 에서 얻 은 잠 금(특히 중요 한 점)을 자동 으로 해제 하고 스 레 드 를 전환 합 니 다.다른 스 레 드 에서 깨 어 나 면 이 스 레 드 는 깨 어 난 후에 스 레 드 가 await()의 위치 에서 계속 아래로 실 행 됩 니 다.따라서 일반적으로 while 순환 에 맞 춰 사용 해 야 합 니 다.만약 에 특정한 스 레 드 가 깨 어 나 면 그 전에 가 져 온 자물쇠 도 다시 가 져 옵 니 다.이때 이 자 물 쇠 는 다른 스 레 드 에 의 해 가 져 왔 고 잠 금 이 풀 리 지 않 았 으 면 깨 우 는 중 오류 가 발생 합 니 다.알 수 없 는 오류 가 발생 할 수 있 으 므 로 volatile 변 수 를 설정 하여 스 레 드 의 운행 상 태 를 검사 해 야 하기 때문에 await()방법 은 앞 뒤로 검 측 해 야 합 니 다.
여기 서 문 제 를 제기 합 니 다.leetcode 에서 왔 습 니 다.condition 류 를 사용 하여 쓰 라 고 요구 합 니 다.
제목:
1 부터 n 까지 이 숫자 를 나타 내 는 문자열 을 출력 할 수 있 는 프로그램 을 만 듭 니 다.하지만:
이 숫자 가 3 으로 나 눌 수 있다 면'fizz'를 출력 합 니 다.
이 숫자 가 5 로 나 눌 수 있다 면'버 즈'를 출력 합 니 다.
이 숫자 가 3 과 5 로 동시에 나 눌 수 있다 면'fizzbuzz'를 출력 합 니 다.
예 를 들 어 n=15,출력:1,2,fizz,4,buzz,fizz,7,8,fizz,buzz,11,fizz,13,14,fizzbuzz.
해법:

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
  static void printFizz(int x){
    System.out.printf("%d:Fizz,
",x); } static void printBuzz(int x){ System.out.printf("%d:Buzz,
",x); } static void printFizzBuzz(int x){ System.out.printf("%d:FizzBuzz,
",x); } static void printaccpt(int x){ System.out.printf("%d,
",x); } static volatile int now=1; static ReentrantLock lock=new ReentrantLock(); static Condition k1=lock.newCondition(); public static void test(int n) { new Thread(()->{ while(now<=n){ lock.lock(); try{ while(now%5==0||now%3!=0){ if(now>n) throw new InterruptedException(); k1.await(); if(now>n) throw new InterruptedException(); } printFizz(now); now++; k1.signalAll(); } catch (InterruptedException e) { break; //e.printStackTrace(); } finally{ lock.unlock(); } } System.out.println("Thread 1 is over"); }).start(); new Thread(()->{ while(now<=n){ lock.lock(); try{ while(now%5!=0||now%3==0) { if(now>n) throw new InterruptedException(); k1.await(); if(now>n) throw new InterruptedException(); } printBuzz(now); now++; k1.signalAll(); } catch (InterruptedException e) { break; // e.printStackTrace(); } finally{ lock.unlock(); } } System.out.println("Thread 2 is over"); }).start(); new Thread(()->{ while(now<=n){ lock.lock(); try{ while(now%5!=0||now%3!=0) { if(now>n) throw new InterruptedException(); k1.await(); if(now>n) throw new InterruptedException(); } printFizzBuzz(now); now++; k1.signalAll(); } catch (InterruptedException e) { break; //Thread.interrupted(); //e.printStackTrace(); } finally{ lock.unlock(); } } System.out.println("Thread 3 is over"); }).start(); new Thread(()->{ while(now<=n){ lock.lock(); try{ while(now%5==0||now%3==0) { if(now>n) throw new InterruptedException(); k1.await(); if(now>n) throw new InterruptedException(); } printaccpt(now); now++; k1.signalAll(); }catch (InterruptedException e){ break; //Thread.interrupted(); //e.printStackTrace(); } finally{ lock.unlock(); } } System.out.println("Thread 4 is over"); }).start(); } public static void main(String[] args) throws InterruptedException { test(30); } }
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기