자바 다 중 스 레 드 의 Executor,ExecutorService,Executors,Callable,Future 와 Future Task

원본 주소:https://www.cnblogs.com/zhrb/p/6372799.html
1.도입부
자바 다 중 스 레 드 를 처음 배 울 때 ThreadRunnable 을 사용 하여 스 레 드 를 만 들 고 시작 합 니 다.다음 예:
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
});
t1.start();

우 리 는 스스로 Thread 대상 을 만 들 고 시작 해 야 합 니 다.
중요 개념:
  • Runnable 을 실현 하 는 종 류 는 하나의 스 레 드 가 아니 라 하나의 임무 로 여 겨 져 야 한다.자바 다 중 스 레 드 에서 우 리 는 반드시 명확 한 이 해 를 가 져 야 한다.임무 와 스 레 드 는 서로 다른 개념 이다.스 레 드(Thread)를 사용 하여 작업 을 수행 할 수 있 지만 작업 은 스 레 드 가 아 닙 니 다.
  • 자바 다 중 스 레 드 에는 두 가지 유형의 작업 이 있 습 니 다.Runnable 형식 작업(반환 값 없 음)과 Callable 형식 작업(반환 값 있 음)이 있 습 니 다.

  • 2.Executor 실행 스 레 드 사용
    일부 기 존 실행 기 는 Thread 대상 을 관리 하 는 데 도움 을 줄 수 있 습 니 다.Thread 대상 을 만 들 고 제어 할 필요 가 없습니다.예 를 들 어 코드 에 new Thread 이나 thread1.start() 을 작성 하지 않 아 도 다 중 스 레 드 를 사용 할 수 있 습 니 다.다음 예:
    ExecutorService exec = Executors.newCachedThreadPool();
    for (int i = 0; i < 5; i++) {//5   
        exec.submit(new Runnable() {
            @Override
            public void run() {            
                System.out.println(Thread.currentThread().getName()+" doing task");
             }
         });
    }
    exec.shutdown();  //     

    출력 은 다음 과 같 습 니 다:
    pool-1-thread-2 doing task
    pool-1-thread-1 doing task
    pool-1-thread-3 doing task
    pool-1-thread-4 doing task
    pool-1-thread-5 doing task

    출력 을 통 해 알 수 있 듯 이 exec 는 스 레 드 탱크 1 의 5 개의 스 레 드 를 사용 하여 이 몇 가지 임 무 를 수행 했다.
    이 예 에서 exec 라 는 Executor 는 관리 임 무 를 맡 고 있 습 니 다.이른바 임 무 는 여기 서 Runnable 인터페이스의 익명 내부 류 를 실현 하 는 것 입 니 다.몇 개의 스 레 드 를 사용 해 야 하 는 지,언제 이 스 레 드 를 시작 해 야 하 는 지,아니면 하나의 스 레 드 로 이 임 무 를 완성 해 야 하 는 지,우 리 는 걱정 할 필요 가 없다.완전히 exec 라 는 실행 기 가 책임 집 니 다.여기 서 exec(new CachedThreadPool)는 수요 에 따라 새 스 레 드 를 만 들 수 있 는 스 레 드 풀 을 가리 키 고 있 습 니 다.
    Executors 는 집행 기 에 해당 하 는 공장 류 로 각종 상용 집행 기 를 포함 하 는 공장 방법 으로 상용 집행 기 를 직접 만 들 수 있다.몇 가지 자주 사용 하 는 실행 기 는 다음 과 같다.Executors.newCachedThreadPool,필요 에 따라 새로운 스 레 드 풀 을 만 들 수 있 습 니 다.스 레 드 탱크 에서 만 든 스 레 드 는 어떤 작업 을 완성 한 후에 다른 작업 을 수행 하 는 데 사 용 될 수 있 습 니 다.Executors.newFixedThreadPool(int nThreads),고정 스 레 드 수 를 다시 사용 할 수 있 는 스 레 드 풀 을 만 듭 니 다.이 스 레 드 탱크 에는 최대 nThread 스 레 드 가 포함 되 어 있 습 니 다.Executors.newSingleThreadExecutor(),하나의 워 커 스 레 드 를 사용 하 는 Executor 를 만 듭 니 다.임무 가 아무리 많아 도 한 라인 으로 만 임 무 를 완성 할 수 있다.Executors.newSingleThreadScheduledExecutor(),단일 스 레 드 실행 프로그램 을 만 듭 니 다.주어진 지연 후 명령 을 실행 하거나 정기 적 으로 실행 할 수 있 습 니 다.
    newSingleThreadExecutor 예 는 다음 과 같 습 니 다.
    ExecutorService exec = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 5; i++) {
        exec.execute(new Runnable() {//execute    Runnable  ,    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
    }
    exec.shutdown();

    출력 은 다음 과 같 습 니 다:
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1

    5 개의 미 션(new Runnable 5 개)이 있 지만 1 개의 스 레 드 로 만 수행 할 수 있 음 을 알 수 있다.
    최고의 실천:우 리 는 기 존의 Executor 또는 Executor Service 실현 류 를 사용 해 야 합 니 다.예 를 들 어 앞에서 말 한 new Cached ThreadPool 은 스 레 드 풀 을 사용 하여 비용 을 낮 출 수 있 습 니 다(새로운 스 레 드 를 만 드 는 데 일정한 대가 가 있 습 니 다).그리고 new Fixed ThreadPool 은 병행 스 레 드 수 를 제한 할 수 있 습 니 다.즉,우 리 는 일반적으로 Executors 의 공장 방법 을 사용 하여 우리 가 필요 로 하 는 실행 기 를 만 드 는 것 이다.
    Executor 와 ExecutorService 의 일반적인 방법
    execute 방법:
    Executor 인 터 페 이 스 는 void execute(Runnable command) 방법 밖 에 없다.방법 성명 에서 우 리 는 Runnable 형식의 대상 으로 들 어 가 는 것 을 볼 수 있다.자주 사용 하 는 예 는 다음 과 같다.
    Executor executor = anExecutor;
    executor.execute(new RunnableTask1());

    그러나 안에서 구체 적 으로 어떻게 실행 하 는 지,스 레 드 실행 여 부 는 해당 하 는 Executor 인터페이스 에서 클래스 를 결정 합 니 다.예 를 들 어 앞의 newCachedThreadPool 은 스 레 드 탱크 를 사용 하여 집행 한다.Executor 는 모든 작업 을 어떻게 실행 하 는 지(스 레 드,스케줄 링 을 어떻게 사용 하 는 지)와 분리 합 니 다.
    submit 방법:ExecutorService 인 터 페 이 스 는 Executor 인 터 페 이 스 를 계승 하여 부모 인터페이스 의 execute 방법 을 확장 했다.자주 쓰 는 submit 방법 이 두 가지 가 있어 요.
    Future> submit(Runnable task) 
     Future submit(Callable task)

    이 두 가지 일반적인 방법 중 하 나 는 Runnable 형식 입 참 을 받 고 하 나 는 Callable 형식 입 참 을 받 는 것 을 볼 수 있 습 니 다.Callable 입 참 은 작업 반환 값 을 허용 하고 Runnable 은 반환 값 이 없습니다.즉,스 레 드 가 되 돌아 오 기 를 원한 다 면 Callable 형식 으로 참여 해 야 한 다 는 것 이다.
    invokeAll 과 invokeAny 방법:
    Callable 작업 을 일괄 실행 합 니 다.이 중 invokeAll 은 모든 작업 이 완료 되면 결 과 를 나타 내 는 Future 목록 입 니 다.invokeany 는 이 작업 의 모든 임 무 를 완성 한 후에 돌아 갑 니 다.두 가지 방법의 반환 결 과 를 통 해 우 리 는 두 가지 방법의 차 이 를 알 수 있다.
     List> invokeAll(Collection extends Callable> tasks)
     T invokeAny(Collection extends Callable> tasks)

    invoke All 은 List 로 돌 아 왔 고 invoke 는 T 으로 돌 아 왔 다.
    shutdown()방법:
    순서대로 닫 기 를 시작 하고 이전에 제출 한 작업 을 수행 하지만 새 작업 은 받 지 않 습 니 다.이 방법 을 실행 하면 스 레 드 탱크 는 작업 이 끝 난 후에 닫 히 고 새로운 작업 을 받 지 않 습 니 다.shutdown() 방법 을 실행 한 후 execute 방법 을 실행 하면 Rejected Execution Exception 을 직접 던 집 니 다.왜 알 았 냐 고 묻 지 마.
    원칙:ExecutorService(라인 풀)가 더 이상 사용 하지 않 는 다 면 자원 을 회수 하기 위해 닫 아야 합 니 다.이것 은 더 이상 사용 하지 않도록 주의해 야 한다.
    상술 한 방법 이 비교적 많 으 니 뒤의 실례 에 맞추어 이해 할 수 있다.execute 방법 과 shutdown 방법 을 먼저 기억 할 수 있 습 니 다.
    3.Callable 과 Future 사용
    호출 가능 인터페이스Runnable 인터페이스 에 있 는 public void run() 방법 은 반환 값 이 없습니다.만약 에 우리 가 스 레 드 연산 후 결 과 를 되 돌려 주 기 를 원한 다 면 Runnable 을 사용 하면 어 쩔 수 없습니다.이때 우 리 는 Callable 을 사용 해 야 한다.Callable 은 반환 값 이 있 는 작업 을 대표 합 니 다.Callable 인 터 페 이 스 를 실현 하 는 클래스 는 다음 과 같 습 니 다.
    class CalcTask implements Callable<String> {
        @Override
        public String call() throws Exception {
            return Thread.currentThread().getName();
        }
    }

    이 작업 은 비교적 간단 합 니 다.바로 현재 스 레 드 의 이름 을 되 돌려 주 는 것 입 니 다.Runnable 에 비해 반환 값 이 있 습 니 다.여기 서 반환 값 유형 은 String 이 고 다른 유형 일 수도 있 습 니 다.
    다음 코드 를 사용 하여 호출 합 니 다:
    ExecutorService exec = Executors.newCachedThreadPool();
    List> taskList = new ArrayList>();
    /*         5    */
    for (int i = 0; i < 5; i++) {
        taskList.add(new CalcTask());
    }
    /*     :           */
    List> resultList = new ArrayList>();
    try {
        /*invokeAll        , submit      */
        resultList = exec.invokeAll(taskList);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    try {
        /* future           */
        for (Future future : resultList) {
            System.out.println(future.get());//get           
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    출력 은 다음 과 같 습 니 다:
    pool-1-thread-1
    pool-1-thread-2
    pool-1-thread-3
    pool-1-thread-4
    pool-1-thread-5

    Future 인터페이스
    위의 예 에서 우 리 는 Future 인 터 페 이 스 를 사용 했다.Future 는 비동기 계산 결 과 를 나타 낸다.그것 은 계산 이 완 료 될 때 까지 계산 하 는 방법 을 검사 하고 계산 결 과 를 얻 는 방법 을 제공 했다.위의 예 에서 exec 실행 기 는 Callable 형식의 작업 목록 을 실행 하고 Futuer 형식의 결과 목록 resultList 를 얻 었 습 니 다.
    get 방법
    계산 이 끝 날 때 까지 기 다 렸 다가 결 과 를 얻 습 니 다.
    isDone 방법
    작업 이 끝 났 는 지 확인 하 는 데 사용 합 니 다.예 는 다음 과 같 습 니 다.
    /*    Callable  */
    Callable callableTask = new Callable() {
        @Override
        public Integer call() throws Exception {
            System.out.println("Calculating 1+1!");
            TimeUnit.SECONDS.sleep(2);//  2 
            return 2;
        }
    }; 
    ExecutorService executor = Executors.newCachedThreadPool();
    Future result = executor.submit(callableTask);
    executor.shutdown();
    while(!result.isDone()){//isDone()             
        System.out.println("       ");
        TimeUnit.SECONDS.sleep(1);//  1 
    }
    try {
        System.out.println("       :"+result.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

    출력 은 다음 과 같 습 니 다:
    Calculating 1+1!
           
           
           :2

    4.FutureTask FutureTask 종 류 는 Future 인터페이스의 실현 이다.Future Task 류 는 Runnable Future 인 터 페 이 스 를 실 현 했 고 Runnable Future 는 Runnable 인터페이스 와 Future 인 터 페 이 스 를 계 승 했 기 때문에:
  • Future Task 는 Runnable 피 스 레 드 로
  • 을 실행 할 수 있 습 니 다.
  • Future 가 들 어 오 는 Callable 대상 의 반환 값 예 는 다음 과 같 습 니 다.
    FutureTask futureTask = new FutureTask<>(new Callable() {
       @Override
       public Integer call() throws Exception {
        System.out.println("futureTask is wokring 1+1!");
        return 2;
       }
    });
    Thread t1 = new Thread(futureTask);//1.    Runnable      
    t1.start();
    try {
       System.out.println(futureTask.get());//2.    Future             
    } catch (ExecutionException e) {
       e.printStackTrace();
    }

    출력 은 다음 과 같 습 니 다:
    futureTask is wokring 1+1!
    2
    에서 알 수 있 듯 이 Future Task 는 반환 값 이 있 는 Runnable 작업 으로 사용 할 수 있 습 니 다.

  • 분석:FutureTask futureTask = new FutureTask<>(new Callable...) 은 Callable 작업 을 Runnable 작업 으로 전환 하면 스 레 드 를 사용 하여 이 작업 을 수행 할 수 있 습 니 다.반면 futureTask.get() 은 Callable 을 Future 로 전환 해 비동기 연산 의 결 과 를 얻 는 셈 이다.
    ExecutorService 실행 기 는 Runnable 과 Callable 형식의 입 참 을 받 는 것 외 에 Future Task 형식 도 받 을 수 있 습 니 다.예 는 다음 과 같 습 니 다.
    FutureTask futureTask = new FutureTask<>(new Callable() {
        @Override
        public Integer call() throws Exception {
            System.out.println("futureTask is wokring 1+1!");
            TimeUnit.SECONDS.sleep(2);
            return 2;
        }
    });
    ExecutorService executor = Executors.newCachedThreadPool();
    executor.submit(futureTask);//     execute,      Runnable    
    executor.shutdown();
    while(!futureTask.isDone()){
        System.out.println("       ,    ");
        TimeUnit.SECONDS.sleep(1);
    }
    try {
        System.out.println("        :"+futureTask.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

    좋은 웹페이지 즐겨찾기