230314 JAVA_Thread

Myung A Lee·2023년 3월 14일
0

JAVA

목록 보기
12/13
post-thumbnail

Java

Thread

Thread란 어떤 Program을 실행하게 해 주는 원동력으로 대표적으로 main(String args) method는 Java 프로그램을 동작하게 해주는 method 이다.

Thread vs Process

Process 역시 Program을 작동시킨다는 점에서 비슷하지만 차이점이 존재한다.

  • Process는 하나 이상의 Thread로 구성되어 각 Thread가 다양한 기능을 작동시킨다.
  • Process는 서로간에 Memory를 공유하지 않지만 Thread는 공유한다.

Multi Thread

지금까지 우리가 사용했던 Main method는 Main Thread를 생성한다. 이 Main Thread는 Work Thread를 생성하여 일을 시킬 수 있으며 우리는 이것을 Multi Thread라고 부른다.

Multi Thread 생성 방법

Runnable Interface 구현

// Job.class
public class Job implements Runable{
	@Override
	public void run() {
		// 스레드에게 시킬 일
    }
}

// Main.class
public class Main {
	public static void main(String[] args) {
		
		// 1. 해야할 일 정하기
		Job job = new Job();
		
		// 2. Work Thread 생성
		// job은 Runnable Interface를 구현 받은 클래스를 객체화 하였으므로
		// 다형성에 의해 Job 타입의 매개변수로 들어갈 수 있다.
		Thread work = new Thread(job);
		
		// 3. Work Thread 실행
		work.start();
		
	}

}


// Main.class (Annonymous 객체 활용 가능)
public class Main {
	public static void main(String[] args) {
        Thread work = new Thread(new Runnable() {

       		@Override
            public void run() {
            	// Work Thread가 해야할 일 지정
            }
		});
	}
}

Thread Class 상속

// Job.class
public class Job extends Thread {
	@Override
	public void run() {
		// 스레드에게 시킬 일
    }
}

// Main.class
public class Main {
	public static void main(String[] args) {
		
		//1. Work Thread 해야할 일 생성		
		// 2. Work Thread 생성 
        // Job이 Thread를 상속 받았기 때문에 다형성에 의해 가능
		Thread work = new Job();
		
		
		// 3. Work Thread 실행
		work.start();
		
	}

}


// Main.class (Annonymous 객체 활용 가능)

public class Main {
	public static void main(String[] args) {
        Thread work = new Thread() {

			@Override
			public void run() {
				// 스레드에게 시킬 일
			}
			
		};
		work.start();
	}
}

Thread Name

기본적으로 Multi Thread에는 생성되는 순서 대로 Thread-0, Thread-1, Thread-2, ..., Thread-n의 이름을 갖는다. Thread의 이름을 지정하고 싶다면 thread.setName("스레드명");, 스레드의 이름을 가져 오고 싶다면 thread.getName();을 사용한다.

setter와 getter method를 통해 접근하는 것으로 우리는 thread class에서 thread의 이름을 private로 캡슐화 하여 관리하는 것을 알수 있다.

MultiThread Scheduling

Multi Thread를 실행하다보면 순서가 제멋대로인 것을 볼 수 있다. Thread는 여러 일을 동시에 주기적으로 처리 해 줄 수 있으나 제어가 어렵다는 문제가 있다. 기본적으로 Thread가 Round Robin 방식을 사용하기 때문이다. 즉, 먼저 시작했어도 더 늦게 끝날 수 있다. 이러한 Thread를 제어하기 위해 다양한 방법을 사용하고 있다.

Round Robin
대표적인 스케줄링 기법 중 하나로 RR은 시분할 시스템을 위해 설계 된 선점형 스케줄링이다. 프로세스들 사이에 우선순위를 두지 않고, 순서대로 시간 단위로 CPU를 할당하는 방식.

Sychronized (동기)

동기화란 내 작업이 끝나기 전에 아무도 접근하지 못하게 막는 다는 의미로 Thread는 Thread간 Memory Share가 가능하기 때문에 발생할 수 있는 데이터의 간섭,오염을 막을 때 사용한다. 가장 대표적인 예로는 Vector와 HashTable 과 같은 Collection이 있다.

동기 vs 비동기
동기와 비동기는 어떤 작업 혹은 그와 연관된 작업을 처리하고자 하는 목적의 차이
동기 : 추구하는 행위 와 목적이 동시에 이뤄진다.
비동기 : 추구하는 행위 와 목적이 다를 수도 있고, 동시에 이루어지지도 않는다.

Sychronized method

이 메서드에서는 오직 하나의 스레드만 입장 가능하도록 한다. 나머지는 줄을 세운다.

Sychronized block

누구나 메서드까지는 들어올 수 있다. 하지만 특정 영역(block)에서는 줄을 세운다.

Thread State

Thread는 생성부터 종료까지의 상태 값이 존재하며 Thread.getState()를 통해 현재 상태를 알 수 있다.

Thread Control

Round Robin Scheduling에 의해 예상한 순서대로 작동하지 않는 Thread의 제어를 위해 다양한 메서드 들이 존재한다.

Thread.sleep(milliseconds)

주어진 milliseconds 동안 Thread를 일시 정지 시킨다.

Thread.yield()

특정스레드에게 제어권을 양보한다. 다만 모든 우선권을 넘기는게 아니라 특정스레드가 응답이 없을시 다시 제어권을 가져온다.

Thread.join()

다른 Thread의 종료를 기다린 후에 실행 할 때 사용한다. ( blocking : 프로그램의 흐름을 막음 )

Thread.wait()

Thread를 일시 정지 상태로 만든다. synchronized 안에서만 호출해야 한다.

Thread.notify()

일시 정지 상태인 Thread 중 하나를 Runnable(실행 대기) 상태로 바꾼다. synchronized 안에서만 호출해야 한다.

Thread.notifyAll()

일시 정지 된 모든 Thread를 Runnable(실행 대기) 상태로 만든다. synchronized 안에서만 호출해야 한다.

public class WorkObj {
	
	// wait(), notify(), notifyAll()은 synchronized 영역 안에서 사용해야 한다. 
	// 그렇지 않으면 동시에 여러개의 스레드가 wait() 상태가 될 수 있다.
	public synchronized void work() {
		System.out.println(Thread.currentThread().getName()+"이 들어옴");
		notify(); // 1. 누군가를 깨움
		try {
			wait();// 스스로는 잠든다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
	}
}

Thread.stop()

Thread를 강제로 종료하는 Method로 현재는 사용중지(deprecated)를 권고 하고 있다. 대체로 StopFlag와 Forced Intterrupt 사용한다.

Stop Flag

Forced Interrupt

강제로 인터럽트 예외를 발생시키는 것
인터럽트 예외 활용 1 : sleep 사용 및 try-catch 활용
인터럽트 예외 활용 2 :Thread.interrupted(); // Thread에 interrupt를 발생시킨 다는 것

Demon Thread

Main Thread 작업을 돕는 보조적인 역할을 수행하며 Work Thread와 마찬가지로 Main Thread에 의해서 생성되지만 Life Cycle에서 차이가 있다. Demon Thread의 Life Cycle은 Main Thread 와 같다. (Work Thread는 Main Thread가 죽어도 계속 활동한다. )

Thread Pool

Thread Pool은 대여소의 역할을 수행하며 Thread를 보유하고 있다가 순서에 따라 빌려 주고 사용 후에 돌려받는다.

Thread Pool 생성

ExecutorService객체를 통해서 생성 되며 객체 생성시 기본적으로 생성 되는 초기 스레드 수, 풀에 최소한 유지해야할 코어 스레드 수, 최대 스레드 수 를 명시 해 줄 수 있다.
1개 이상의 스레드가 추가되어 60초 동안 놀고 있으면 스레드를 제거해 버린다.
대기 -> 대여 -> 반납 -> 60초 동안 아무도 안빌려 가면 -> 삭제

newCachedThreadPool()

ExecutorService pool = Executors.newCachedThreadPool();

newFixedThreadPool(n)

ExecutorService pool = Executors.newFixedThreadPool(nThreads);

Thread Pool 작업 할당

Thread Pool 의 작업은 Runnable 과 Callable 로 생성 하며 두 방법의 차이는 return값의 유무이다.

Runnable

Runnable Task = new Runnable( ){
	@Override
	public void run( ){
		/* 스레드가 처리할 내용 */
	}
}
// 실행문
pool.execute(task); 

Callable (return O)

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

// 실행 문 : return 값은 Future<T> 타입 변수로 받아야하며
// get() 메서드를 통해 T 타입 변수에 담아 출력 할 수 있다. 
Future<T> result = pool.submit(task);
T msg = result.get();
  

Thread Pool 실행

생성된 작업은 submit()과 execute()로 실행 가능하다.

submit()

Return 타입이 void로 작업처리의 결과를 받지 않아도 될 때 사용하며 작업처리 도중 예외 상황에 스레드를 종료하고 풀에서 스레드를 제거한다.

execute()

Return 타입이 Future로 작업처리의 결과과 반환되며 작업처리 도중 예외 상황에도 스레드를 종료하지 않고 재사용 한다.

Thread Pool Blocking

Future 객체를 가져오는 get() 메서드는 join()과 같은 역할로 반환값이 반환될 때 까지 Blocking 역할을 수행한다. 반환값이 존재하지 않는 Runnable Task에서도 이는 사용이 가능하다.

Future<Integer> result = pool.submit(callable);

Thread Pool 생성

Thread Pool 의 Thread 는 Main Thread가 종료되더라도 계속 실행 상태로 남아 있기때문에 종료 메서드가 따로 존재 한다.

shutdown()

처리 중인 작업 내용을 모두 마무리하고 스레드 풀을 종료 시킨다.

shutdownNow()

처리 중인 작업의 마무리 여부와 관계 없이 interrupt를 발생시켜 중지시킨다.

awaitTermination(long timeout, TimeUnit)

shutdown() 메서드 호출 이후 timeout 시간 초과 여부에 따라 시간내에 처리 될 시 true, 시간내 미처리시 interrupt 하고 false를 반환한다.

0개의 댓글