<자바>
메인(main)스레드
: 모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하면서 시작된다.
: 코드의 실행 흐름 -> 스레드
: main 메소드에서 return 문을 만나면 실행이 종료된다,
: JVM이 메인 스레드를 만듬
: main스레드는 작업 스레드들을 만들어서 병렬로 코드를 실행할수 있다.
=> 멀티 스레드를 생성해서 멀티 태스킹을 수행한다.
프로세스의 종료
싱글 스레드; 메인 스레드가 종료하면 프로세스도 종료된다.
멀티 스레드 : 실행중인 스레드가 하나라도 있다면, 프로세스는 종료되지 X
=> 메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행중이라면
프로세스는 종료되지 않음
=> 멀티스레드일 경우 모든 스레드가 종료 되어야지 멈춤
스레드 풀
스레드 폭증
1: 병렬 작업 처리가 많아지면 스레드의 개수가 증가
2. 스레드 생성과 스케쥴링으로 인해 CPU가 바빠지고 메모리 사용량이 늘어남
3. 어플리케이션의 성능이 급격히 저하됨
스레드 풀(ExecutorService)
: 작업 처리에 사용되는 스레드를 제한된 개수만큼 미리 생성
: 작업 큐에 들어오는 작업들을 하나씩 스레드가 맡아서 처리
: 작업 처리가 끝난 스레드는 작업 결과를 애플리케이션을 전달
: 스레드는 다시 작업큐에서 새로운 작업을 가져와 처리
ExecutorService 인터페이스와 Executors 클래스
: 스레드풀을 생성하고 사용할수 있도록 java.util.concurrent 패키지에서 제공
: Executors의 정적 메소드를 이용해서 ExecutorService 구현 객체 생성
: 스레드 풀 = ExecutorService 객체
스레드풀 생성
다음 두가지 메소드 중 하나로 간편 생성
코어 스레드수
: 작업량이 많아지게 되면 스레드가 늘어나다가 작업량이 줄어들게 되면 놀게 되는 스레드가 발생하게 된다.
코어 스레드는 놀게 되는 스레드가 있을경우 즉, 사용하지 않았을때도 유지되어야 하는 스레드를 말한다.
- newCachedThreadPool()
- int값이 가질수 있는 최대 값만큼 스레드가 추가되나, 운영체제의 상황에 따라 달라진다.
- 1개 이상의 스레드가 추가되었을 경우
- 60초 동안 추가된 스레드가 아무 작업을 하지 않으면
- 추가된 스레드를 종료하고 풀에서 제거한다.
- ExecutorService executorService = Executors.newCachedThreadPool();
- new FixedThreadPool(int nThreads)
- 코어 스레드 개수와 최대 스레드 개수가 매개값으로 준 nThread이다.
- 스레드가 작업을 처리하지 않고 놀고 있더라도 스레드 개수가 줄지 않는다.
ThreadPoolExecutor을 이용한 직접 생성
: newCachedThreadPool()와 newFixedThreadPool(int nThreads)가 내부적으로 생성
: 스레드의 수를 자동으로 관리하고 싶을 경우 직접 생성해서 사용
스레드풀 종료
- 스레드풀의 스레드는 기본적으로 데몬 스레드가 아니다
- main 스레드가 종료되더라도 스레드풀의 스레드는 작업을 처리하기 위해 계속 실행되므로 애플리케이션은 종료되지 않는다.
- 따라서 스레드풀을 종료해서 모든 스레드를 종료시켜야 한다.
- 스레드풀 종료 메소드
- void shutdown()
- 현재 처리 중인 작업뿐만 아니라 작업큐에 대기하고 있는 모든 작업을 처리한 뒤에 스레드풀을 종료시킨다.
- List<Runnable> shutdownNow()
- 현재 작업 처리 중인 스레드를 interrupt 해서 작업중지를 시도하고 스레드풀을 종료시킨다.
- 리턴값은 작업큐에 미처리된 작업(Runnable)의 목록이다.
- 사용하는게 좋은 방법은 아니다.
- boolean awaitTermination(long timeout,TimeUnit unit)
- shutdown() 메소드 호출 이후, 모든 작업 처리를 timeout 시간 내에 완료하면 true를 리턴하고, 완료하지 못하면
- 작업 처리 중인 스레드를 interrupt 하고 false를 리턴한다.
작업 생성
: 하나의 작업은 Runnable 또는 Callable 객체로 표현한다.
: Runable과 Callable의 차이점
- 작업 처리 완료 후 리턴값이 있느냐 없느냐 이다.
Runnable 구현 클래스 | Callable 구현 클래스 |
Runnable task = new Runnable(){ @Override public void run(){ 스레드가 처리할 작업 내용 } } | Callable<T> task = new Callable<T> { @Override public T call() throws Exception{ //스레드가 처리할 작업 내용 return T; } } |
: 스레드풀에서 작업 처리
- 작업큐에서 Runnable 또는 Callable 객체를 가져와 스레드로 하여금 run()과 call()메소드를 실행토록 하는 것이다.
작업처리 요청
- ExecutorService의 작업 큐에 Runnable 또는 Callable 객체를 넣는 행위를 말한다.
- 작업 처리 요청을 위해 ExecutorService는 다음 두 가지 종류의 메소드를 제공한다.
리턴타입 | 메소드명(매개변수) | 설명 |
void | execute(Runnable command) |
|
Future<?> Future<V> Future<V> | submit(Runnable task) submit(Runnable task, V result) submit(Callable <V> task) |
|
- 작업 처리 도중 예외가 발생할 경우
- execute()
- 스레드가 종료되고 해당 스레드는 제거된다.
- 따라서 스레드풀은 다른 작업 처리를 위해 새로운 스레드를 생성한다.
- submit()
- 스레드가 종료되지 않고 다음 작업을 위해 재사용된다.
=> submit이 더 좋다.
=> 리턴값이 없는것도 submit을 쓰는게 더 좋다.
블로킹 방식의 작업 완료 통보 받기
리턴타입 | 메소드명(매개변수) | 설명 |
Future<?> | submit(Runnable task) |
|
Future<V> | submit(Runnable task, V result) | |
Future<V> | submit(Callable<V> task) |
- Future
- 작업 결과가 아니라 지연 완료(pending completion) 객체
- 작업이 완료될까지 기다렸다가 최종 결과를 얻기 위해서 get() 메소드 사용
리턴타입 | 메소드명(매개변수) | 설명 |
V | get() | 작업이 완료될 때까지 블로킹되었다가 처리 결과 V를 리턴 , 작업이 완료될때 까지 기다리겠다는 의미 (Blocking) |
V | get(long timeout, TimeUnit unit) | timeout 시간동안 작업이 완료되면 결과 V를 리턴하지만 , 작업이 완료되지 않으면 TimeoutException을 발생시킴 |
메소드 | 작업 처리 완료후 리턴 타입 | 작업처리 도중 예외 발생 |
submit(Runnable task) | future.get() ->null | future.get() -> 예외 발생 |
submit(Runnable task,Integer result) | future.get() -> int 타입 값 | future.get() -> 예외 발생 |
submit(Callable<String> task) | future.get() -> String 타입 값 | future.get() -> 예외 발생 |
- Future의 get()은 UI 스레드에서 호출하면 안된다.
- UI를 변경하고 이벤트를 처리하는 스레드가 get() 메소드를 호출하면
- 작업을 완료하기 전까지는 UI를 변경할 수도 없고 이벤트도 처리할 수 없게 된다.
- 작업 처리 결과를 외부 객체에 저장
Result result = ""; Runnable task = new Task(result); Future<Result> future = executorService.submit(task,result); result = future.get(); | class Task implements Runnable{ Result result; Task(Result result) { this.result = result;} @Override public void run(){ 작업코드 처리 결과를 result에 저장 } } |
작업 완료순으로 통보 받기
- 작업 요청 순서대로 작업 처리가 완료되는 것은 아니다.
- 작업의 양과 스레드 스케쥴링에 따라서 먼저 요청한 작업이 나중에 완료되는 경우도 발생
- 여러 개의 작업들이 순차적으로 처리될 필요성이 없고,
- 처리 결과도 순차적으로 이용할 필요가 없다면
- 작업 처리가 완료된 것부터 결과를 얻어 이용하는 것이 좋다.
- 스레드풀에서 작업 처리가 완료된 것만 통보 받는 방법
- CompletionService는 처리 완료된 작업을 가져오는 poll()과 take() 메소드를 제공한다.
리턴타입 | 메소드명 | 설명 |
Future<V> | poll() | 완료된 작업의 Future를 가져옴 완료된 작업이 없다면 즉시 null을 리턴함 |
Future<V> | poll(long timeout,TimeUnit unit) | 완료된 작업의 Future를 가져옴 완료된 작업이 없다면 timeout까지 블로킹됨 |
Future<V> | take() | 완료된 작업의 Future를 가져옴 완료된 작업이 없다면 있을 때까지 블로킹됨 |
Future<V> | submit(Callable<V> task) | 스레드풀에 Callable 작업 처리 요청 |
Future<V> | submit(Runnable task, V result) | 스레드풀에 Runnable 작업 처리 요청- |
- CompletionService 객체 얻기
ExecutorService executorService = Executors.newFixedThreadPool(2);
CompletionService<V> completionService = new ExecutorCompletionService<V>(executorService);
- 작업 처리 요청 방법
- poll()과 take() 메소드를 이용해서 처리 완료된 작업의 Future을 얻으려면
- CompletionService의 submit() 메소드로 작업 처리 요청을 해야한다.
콜백 방식의 작업 완료 통보 받기
- 콜백이란?
- 애플리케이션이 스레드에게 작업 처리를 요청한 후 , 다른 기능을 수행할 동안,
- 스레드가 작업을 완료하면 애플리케이션의 메소드를 자동 실행하는 기업을 말함
- 이 때 자동 실행되는 메소드를 콜백 메소드라고 한다.
- 작업 완료 통보 얻기: 블로킹 vs 콜백
- 콜백 객체와 콜백 하기
- 콜백 객체: 콜백 메소드를 가지고 있는 객체
'프로그래밍 > 자바' 카테고리의 다른 글
[자바] 멀티쓰레드 Runnable과 Callable 사용방법 (0) | 2019.01.28 |
---|---|
람다식이란? (0) | 2019.01.28 |
[JAVA] 데몬(daemon) 스레드 정리 (0) | 2019.01.27 |
Thread와 Process!! (0) | 2019.01.25 |
[자바] 네트워크 통신시 주의할점 (0) | 2019.01.23 |