Java Thread 관련 정리 from 이것이 자바다(책)

Updated:

Java Thread 관련 정리

Thread

스레드의 동시성(Concurrency)병렬성(Parallelism)의 단어 차이

  1. 동시성(Concurrency) : 하나의 코어에서 멀티 스레드가 번갈아 가면서 실행하는 것을 의미
  2. 병렬성(Parallelism) : 멀티 작업을 위해, 멀티 코어에서 개별 스레드를 동시에 실행하는 것을 의미

즉, 싱글 코어 CPU를 이용한 멀티 스레드 작업은 병렬적으로 실행되는 것처럼 보이지만, 실제로는 번갈아 가면서 실행하는 동시성 작업이다.

만약, 스레드의 갯수 > 코어의 수 이면, 스레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정해야함, 이것을 스레드 스케줄링이라 한다.

자바의 스케줄링은 우선순위(Priority) 방식과 순환 할당(Round Robin) 방식이 있다. 근데, 우선순위 방식은, 개발자가 코드로 제어가능, 그러나 순환 할당은 불가능

우선 순위변경 → thread.setPriority(우선순위)

공유 객체를 사용할때, 여러 스레드가 접근을 하는 경우가 생길 수 있다. 이때, 여러개의 스레드를 동시에 접근하게 되면, 각 스레드가 작업했던 결과물이 제대로 반영이 되지않게 될것이다. 그래서 synchronized 키워드를 이용하여, 하나의 스레드만 실행할수 있게 제한한다

synchronized 키워드는 인스턴스와 정적 메소드 어디든 붙일수있다.

  • sleep : 주어진 시간동안 일시정지
  • yield : 다른 스레드에게 실행 양보
  • join : 다른 스레드의 종료를 기다림
  • wait, notify, notifyAll : 스레드 간 협업, 단 이 메소드들은 동기화 메소드 혹은 동기화 블록 내에서만 사용이 가능함

데몬 스레드(Daemon Thread) : 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드, 그래서 주 스레드가 종료되면 데몬 스레드는 강제적으로 종료된다, 왜냐하면 주 스레드의 보조 역할을 수행해야 하는데, 주 스레드가 없으니까 ㅇㅇ

ex) 워드프로세스의 자동 저장, 미디어 플레이어 동영상 및 음악 재생, 가비지 컬렉터 등

이 기닝들은 주 스레드(워드 프로세스, 미디어 플레이어, JVM)이 종료되면 같이 종료 됨

daemonthread.setDaemon(true); // 데몬 스레드 설정
daemonthread.start(); // 데몬 스레드 스타트
daemonthread.isDaemon(); // 데몬 스레드인지 아닌지 ㅇㅇ

단, start()를 호출하고 나서, setDaemon(true)를 설정하면 IllegalThreadStateException이 발생한다고 한다


스레드 그룹(ThreadGroup) : 스레드를 묶어서 관리할 목적으로 이용된다. JVM이 실행 되면 system 스레드 그룹 만들고, JVM 운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킴

우리가 생성하는 작업 스레드는 대부분 main 스레드가 생성하므로, 기본적으로는 main 스레드 그룹에 속함

ThreadGroup tg = new ThreadGroup(String name); //name을 가진 ThreadGroup을 만든다, 단 만들어진 위치의 스레드가 속한 그룹의 하위그룹으로 생성이 된다
ThreadGroup tg = new ThreadGroup(ThreadGroup parent,String name); // 부모 스레드 그룹을 지정해서, 부모 스레드 하위 그룹으로 생성이 된다
// 부모 스레드 그룹이 있으면, 부모 스레드 그룹이 죽으면 뭐 같이 죽나? (의문)

Thread t = new Thread(ThreadGroup group, Runnable target);
Thread t = new Thread(ThreadGroup group, Runnable target, String name);
Thread t = new Thread(ThreadGroup group, Runnable target, String name, long stackSize);
Thread t = new Thread(ThreadGroup group, String name);
// String 타입의 name은 스레드의 이름, long 타입의 stackSize는 JVM이 이 스레드에 할당할 stack 크기

스레드풀(ThreadPool) : 작업 처리에 사용되는 스레드를 제한된 갯수만큼 정해놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리한다. 그래서 작업 처리 요청이 폭증되어도 스레드의 전체 갯수가 늘어나지 않으므로 애플리케이션의 성능이 급격히 저하되지 않는다

java.util.concurrent 패키지에서 ExecutorService 인터페이스와 Executors 클래스를 제공한다. Executors의 정적 메소드를 이용해 ExecutorService 구현 객체를 만들 수 있다.

ExecutorService threadPool = new ThreadPoolExecutor(
				3, // 코어 스레드 갯수
				100, // 최대 스레드 갯수
				120L, // 놀고 있는 시간
				TimeUnit.SECONDS, // 놀고 있는 시간 단위
				new SynchronousQueue<Runnable>()// 작업큐
);
// 초기 스레드 갯수 0개, 코어 스레드 갯수 3개, 최대 스레드 갯수가 100개, 코어 스레드 3개를 제외한
// 나머지 추가된 스레드가 120초 동안 놀고 있을 경우 해당 스레드를 제거해서 스레드 수를 관리한다

스레드풀의 스레드는 기본적으로 데몬 스레드가 아니니까, main Thread가 종료되어도 작업을 처리하기 위해 계속 실행 상태로 남아있다.

Java_ThreadPool

작업 생성 : Runnable or Callable 구현 클래스로 표현한다. 둘의 차이는 작업 처리 완료 후 리턴값이 있느냐, 없느냐의 차이이다.

Runnable task = new Runnable() {
		public void run() {
				// 스레드가 처리할 작업 내용
		}
}

Callable<T> task = new Callable<T> {
				public T call() throws Exception {
							// 스레드가 처리할 작업 내용
							return T;
				}
}

Java_ThreadPool_Future

Future 객체는 작업 결과가 아니라 작업이 완료될때까지 기다렸다가(지연했다가 = 블로킹되었다가) 최종 결과를 얻는데 사용됨, 흔히 그래서 Future를 지연 완료(pending completion) 객체라고 부르기도한다. get()메소드를 이용하면 된다.

여기서 get메소드를 실행한 스레드는 작업을 완료할때까지는 블로킹이 된다. 그래서 또다른 get메소드를 실행을 동시에 할때는 새로운 스레드가 필요하게 된다는것을 알아야 한다


스레드 풀에서 작업 완료순으로 먼저 된것들을 통보해주는 방법 : CompletionService를 이용하는것

ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
CompletionService<V> completionService = new ExecutorCompletionService<V>(es);

completionService.submit(Callable<V> task);
completionService.submit(Runnable task, V result);

Callback : 애플리케이션이 스레드에게 작업 처리를 요청 한후, 스레드가 작업을 완료하면 특정 메소드를 자동으로 실행하는 기법
마치 커피숍에 가서, 커피를 주문하고 진동벨을 받는것을, 콜백이 등록되었다라고 보면된다. 그리고 커피가 완료되서 진동벨이 울리는것을
콜백이 완료되어 이벤트로 호출이 된다는 것이라 보면 된다.

위와 관련한 모든 예제들은 아래 링크 주소에 있으니 참고해주시면 감사하겠습니다.
https://github.com/goodgood619/ThisIsJava

Leave a comment