(2) 스 레 드 의 응용 및 도전

문장 소개
지난 글 에서 우 리 는 프로 세 스 와 스 레 드 의 발전 역사, 스 레 드 의 생명 주기, 스 레 드 의 장점 과 사용 장면 을 이해 했다. 이 편 은 자바 차원 에서 스 레 드 의 사용 을 더욱 잘 알 게 되 었 다.
콘 텐 츠 탐색
  • 동시 프로 그래 밍 도전
  • 스 레 드 가 자바 에서 의 사용
  • 동시 프로 그래 밍 도전
    다 중 스 레 드 를 도입 하 는 목적 은 첫 번 째 편 에서 언급 한 바 와 같이 CPU 를 충분히 이용 하기 위해 프로그램 이 더 빨리 실행 되 는 것 이다. 물론 시작 하 는 스 레 드 가 많 을 수록 좋다 는 것 은 아니다.실제 다 중 스 레 드 를 사용 할 때 매우 많은 도전 에 직면 하 게 된다.
    스 레 드 보안 문제
    스 레 드 보안 문제 값 은 여러 스 레 드 가 같은 대상 에 접근 할 때 이러한 운행 환경 에서 사용 하 는 스 케 쥴 링 방식 이나 이런 스 레 드 가 어떻게 교체 되 는 지 고려 하지 않 으 면 코드 에서 동기 화 작업 이 필요 없 는 상황 에서 이런 유형 은 정확 한 행 위 를 나 타 낼 수 있다.그러면 이 종 류 는 스 레 드 가 안전 한 것 입 니 다. 예 를 들 어 아래 의 코드 는 하나의 사례 모델 입 니 다. 코드 의 주석 에서 여러 스 레 드 가 동시에 방문 하면 여러 개의 인 스 턴 스 가 나타 납 니 다.단 례 의 효 과 를 실현 할 수 없 게 하 다
    public class SingletonDemo {
       private static SingletonDemo singletonDemo=null;
       private SingletonDemo(){}
        public static SingletonDemo getInstance(){
            if(singletonDemo==null){/***      ***/
               singletonDemo=new SingletonDemo();
            }
            return singletonDemo;
        }
    }

    일반적으로 우 리 는 다 중 스 레 드 프로 그래 밍 중의 스 레 드 안전 문 제 를 다음 과 같은 세 가지 로 분류 하고 모든 문제 의 본질 에 대해 후속 적 인 글 에서 우 리 는 단독으로 설명 할 것 이다.
  • 원자 성
  • 가시 성
  • 질서 성
  • 질문
    단일 핵심 CPU 구조 에서 다 중 스 레 드 의 운행 은 CPU 시간 편 전환 을 바탕 으로 하 는 의사 병렬 입 니 다.시간 이 매우 짧 기 때문에 사용 자 는 여러 스 레 드 가 병행 되 는 줄 알 았 다.한 번 의 컨 텍스트 전환 은 실제 적 으로 현재 스 레 드 가 시간 편 을 실행 한 후에 다른 스 레 드 로 전환 하고 현재 스 레 드 가 실 행 된 상 태 를 저장 하 는 과정 입 니 다.컨 텍스트 전환 은 스 레 드 의 실행 속도 에 영향 을 주 고 시스템 에 있어 서 대량의 CPU 시간 을 소모 한 다 는 것 을 의미 합 니 다.
    컨 텍스트 전환 줄 이기
  • 잠 금 이 없 는 동시 프로 그래 밍 은 다 중 스 레 드 경쟁 잠 금 시 대량의 문맥 전환 을 초래 할 수 있다.자 물 쇠 를 사용 하여 병발 문 제 를 해결 하지 않 으 면 상하 문 전환 을 줄 일 수 있다
  • CAS 알고리즘, CAS 는 낙관적 인 잠 금 메커니즘 으로 잠 금 을 추가 할 필요 가 없다
  • 하드웨어 자원 과 일치 하 는 스 레 드 수 사용
  • 자물쇠
    스 레 드 안전 문 제 를 해결 하 는 장면 에서 우 리 는 자 물 쇠 를 사용 하 는 것 을 비교적 많이 고려 할 것 이다. 왜냐하면 그것 은 사용 이 비교적 간단 하기 때문이다.그러나 자물쇠 의 사용 이 적당 하지 않 으 면 자물쇠 가 잠 길 가능성 을 초래 할 수 있다. 자물쇠 가 잠 겨 있 으 면 비교적 심각 한 문제 가 발생 할 수 있다. 자물쇠 가 생 긴 스 레 드 는 자물쇠 자원 을 계속 점용 하고 다른 자 물 쇠 를 가 져 오 려 는 스 레 드 도 잠 겨 서 시스템 이 붕 괴 될 수 있다.
    다음은 자물쇠 가 잠 긴 간단 한 사례 입 니 다.
    public class DeadLockDemo {
        //     
        private final Object lockA = new Object();
        private final Object lockB = new Object();
        private void deadLock(){
            new Thread(()->{
                synchronized (lockA){
                    try {
                        Thread.sleep(4000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lockB){
                        System.out.println("Lock B");
                    }
                }
            }).start();
            new Thread(()->{
                synchronized (lockB){
                    synchronized (lockA){
                        System.out.println("Lock A");
                    }
                }
            }).start();
        }
        public static void main(String[] args) {
            new DeadLockDemo().deadLock();
        }
    }
    

    jstack 를 통 해 잠 금 분석
    1. 우선 jps 를 통 해 현재 실행 중인 프로 세 스 의 pid 가 져 오기
    6628 Jps
    17588 RemoteMavenServer
    19220 Launcher
    19004 DeadLockDemo

    2. jstack 스 택 정 보 를 인쇄 하고 jstack 19004 를 입력 하면 다음 로 그 를 인쇄 합 니 다. 잠 금 된 정보 알림 을 뚜렷하게 볼 수 있 습 니 다.
    Found one Java-level deadlock:
    =============================
    "Thread-1":
      waiting to lock monitor 0x000000001d461e68 (object 0x000000076b310df8, a java.lang.Object),
      which is held by "Thread-0"
    "Thread-0":
      waiting to lock monitor 0x000000001d463258 (object 0x000000076b310e08, a java.lang.Object),
      which is held by "Thread-1"
    

    자물쇠 해결 수단
    1. 여러 스 레 드 가 같은 순서 로 자 물 쇠 를 가 져 올 수 있 도록 합 니 다.
    2. 자 물 쇠 를 가 져 오 는 시간 초과 설정, 설정 시간 초과 후 자동 방출
    3. 잠 금 검사
    자원 제한
    자원 제한 은 주로 하드웨어 자원 과 소프트웨어 자원 을 말 하 는데 다 중 스 레 드 응용 을 개발 할 때 프로그램의 실행 속 도 는 이 두 자원 에 제한 을 받는다.하드웨어 의 자원 제한 은 디스크, CPU, 메모리, 네트워크 가 아 닙 니 다.소프트웨어 자원 의 제한 이 매우 많다. 예 를 들 어 데이터베이스 연결 수, 컴퓨터 가 지원 할 수 있 는 최대 연결 수 등 자원 제한 으로 인 한 문제 의 가장 직관 적 인 표현 은 바로 앞에서 말 한 문맥 전환 이다. 즉, CPU 자원 과 스 레 드 자원 의 심각 한 불 균형 으로 인해 빈번 한 문맥 전환 이 발생 하고 오히려 프로그램의 운행 속도 가 떨 어 질 수 있다.
    자원 제한의 주요 해결 방안 은 부족 한 것 을 보충 하 는 것 이다.CPU 가 부족 하면 CPU 핵심 수 를 증가 시 킬 수 있 습 니 다.한 대의 기계 의 자원 이 유한 하면 여러 대의 기 계 를 증가 시 켜 군집 을 만든다.
    자바 에서 스 레 드 사용
    자바 에서 다 중 스 레 드 를 실현 하 는 방식 은 비교적 간단 하 다. 자바 에서 매우 편리 한 API 를 제공 하여 다 중 스 레 드 를 실현 하기 때문이다.1. Thread 클래스 를 계승 하여 다 중 스 레 드 실현 2. Runnable 인터페이스 실현 3. Callable 인터페이스 가 Future 포장 기 를 통 해 Thread 스 레 드 를 만 드 는 것 을 실현 합 니 다. 이것 은 반환 값 이 있 는 스 레 드 입 니 다. 4. 스 레 드 탱크 ExecutorService 를 사용 합 니 다.
    Thread 클래스 계승
    Thread 클래스 를 계승 하고 run 방법 을 다시 쓰 십시오. run 방법 에서 현재 스 레 드 가 실행 해 야 할 논 리 를 작성 합 니 다.마지막 으로 스 레 드 인 스 턴 스 start 방법 으로 스 레 드 를 시작 합 니 다.
    public class ThreadDemo extends Thread{
        @Override
        public void run() {
            //  run  ,           
            System.out.println("Hello world");
        }
        public static void main(String[] args) {
            ThreadDemo threadDemo=new ThreadDemo();
            threadDemo.start();
        }
    }
    

    Thread 클래스 는 Runnable 인 터 페 이 스 를 실 현 했 기 때문에 Thread 자신 도 하나의 스 레 드 인 스 턴 스 입 니 다. 그러나 우 리 는 new Thread (). start () 로 스 레 드 를 시작 할 수 없습니다. 이 유 는 간단 합 니 다. Thread 클래스 의 run 방법 은 실제 적 인 의미 가 없습니다. 구조 함 수 를 통 해 보 내 온 다른 Runnable 실현 클래스 를 호출 하 는 run 방법 일 뿐 입 니 다.이 구체 적 인 프레젠테이션 은 Runnable 인터페이스 코드 에서 볼 수 있 습 니 다.
    public
    class Thread implements Runnable {
        /* What will be run. */
        private Runnable target;
        ...
        @Override
        public void run() {
            if (target != null) {
                target.run();
            }
        }
        ...
    

    Runnable 인터페이스 구현
    스 레 드 를 사용 해 야 하 는 클래스 가 다른 클래스 를 계승 했다 면 자바 의 단일 계승 원칙 에 따라 Thread 류 를 계승 하여 스 레 드 를 실현 할 수 없 기 때문에 Runnable 인 터 페 이 스 를 실현 하여 다 중 스 레 드 를 실현 할 수 있 습 니 다.
    public class RunnableDemo implements Runnable{
        @Override
        public void run() {
            //  run  ,           
            System.out.println("Hello world");
        }
        public static void main(String[] args) {
            RunnableDemo runnableDemo=new RunnableDemo();
            new Thread(runnableDemo).start();
        }
    }
    

    위의 코드 에서 Runnable 인 터 페 이 스 를 실현 하고 run 방법 을 다시 썼 습 니 다.이 어 Runnable Demo 라 는 스 레 드 를 시작 하기 위해 서 는 Thread 류 를 예화 해 야 합 니 다. 구조 적 방법 으로 Runnable 인터페이스 실현 류 를 전달 하여 시작 해 야 합 니 다. Thread 의 run 방법 은 target. run 을 호출 하여 현재 스 레 드 를 실행 하고 코드 는 위 에 있 습 니 다.
    Callable 인터페이스 구현
    일부 다 중 스 레 드 가 사용 하 는 장면 에서 우 리 는 가끔 비동기 스 레 드 가 실 행 된 후의 피드백 결 과 를 얻어 야 한다. 아마도 메 인 스 레 드 는 하위 스 레 드 의 실행 결 과 를 얻어 다른 업무 논 리 를 처리 해 야 할 것 이다. 아마도 스 레 드 가 실 행 된 상 태 를 알 아야 할 것 이다.그러면 Callable 인 터 페 이 스 는 이 기능 을 잘 실현 할 수 있 습 니 다.
    public class CallableDemo implements Callable{
        @Override
        public String call() throws Exception {
            return "hello world";
        }
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Callable callable=new CallableDemo();
            FutureTask task=new FutureTask<>(callable);
            new Thread(task).start();
            System.out.println(task.get());//        
        }
    }
    

    위의 코드 사례 에서 마지막 줄 task. get () 은 스 레 드 의 반환 값 을 가 져 오 는 것 입 니 다. 이 과정 은 막 혔 습 니 다. 하위 스 레 드 가 실행 되 지 않 았 을 때 메 인 스 레 드 는 결과 가 돌아 올 때 까지 계속 막 힙 니 다.
    스 레 드 풀 사용
    자주 스 레 드 를 만 들 고 스 레 드 를 없 애 는 성능 비용 을 줄 이기 위해 실제 사용 할 때 우 리 는 스 레 드 탱크 로 스 레 드 를 만 들 것 입 니 다. 여기 서 저 는 다 중 스 레 드 의 장점 과 원 리 를 전개 하지 않 고 후속 적 인 글 에서 따로 설명 하 겠 습 니 다.
    public class ExecutorServiceDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //             
            ExecutorService pool = Executors.newFixedThreadPool(1);
            Future future=pool.submit(new CallableDemo()); 
            System.out.println(future.get());
        }
    }
    

    pool. submit 는 반환 값 이 있 는 스 레 드 인 스 턴 스 를 전달 할 수 있 고 반환 값 이 없 는 스 레 드 인 스 턴 스 를 전달 할 수 있 습 니 다. 소스 코드 는 다음 과 같 습 니 다.
    /*01*/Future> submit(Runnable task);
    /*02*/ Future submit(Runnable task, T result);
    /*03*/ Future submit(Callable task);
    

    좋은 웹페이지 즐겨찾기