자바의정석 ch13

soso·2023년 2월 24일
0
post-thumbnail

Chapter13 쓰레드

프로세스(process)와 쓰레드(thread)

프로세스(process) : 실행중인 프로그램, 자원과 쓰레드로 구성되어있다.
쓰레드(thread) : 프로세스 내에서 실제 작업을 수행한다.
모든 프로세스는 최소한 하나의 쓰레드를 가지고 있다.

프로세스 : 쓰레드 = 공장 : 일꾼

멀티쓰레딩의 장단점

대부분의 프로그램이 멀티 쓰레드로 작성되어 있다.

멀티쓰레딩의 장점

  • CPU의 사용률을 향상시킨다.
  • 자원을 보다 효율적으로 사용할 수 있다.
  • 사용자에 대한 응답성이 향상된다.
  • 작업이 분리되어 코드가 간결해진다.

멀티쓰레딩의 장점

  • 동기화(synchronization)에 주의해야 한다.
  • 교착상태(dead-lock)가 발생하지 않도록 주의해야 한다.
  • 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야한다.

쓰레드의 구현과 실행

  1. Thread클래스를 상속
class MyThread extends Thread{
	public void run(){ /* 작업내용 */} //Thread클래스의 run()을 구현
  }
  1. Runnable인터페이스를 구현
class MyThread implements Runnable{
	public void run(){ /* 작업내용 */} //Runnable인터페이스의 run()을 구현
  }

쓰레드의 실행 - start()

쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다.

ThreadEx1_1 t1 = new ThreadEx1_1(); // 쓰레드 t1을 생성한다.
ThreadEx1_1 t2 = new ThreadEx1_1(); // 쓰레드 t2를 생성한다.
t1.start (); //쓰레드 +1을 실행시킨다.
t2.start (); //쓰레드 t2를 실행시킨다.

한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다.
즉, 하나의 쓰레드에 대해 start()가 한번만 호출될수 있다.

start()와 run()

main메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것일 뿐이다.

반면에 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호스택(call stack)을 생성한 다음에 run()을 호출해서, 생성된 호출스택에 run()이 첫 번째로 올라가게 한다.

모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문에, 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.

main쓰레드

▶main메서드의 코드를 수행하는 쓰레드
▶쓰레드는 '사용자 쓰레드'와'데몬 쓰레드'두 종류가 있다.

실행 중인 사용자 쓰레가 하나도 없을 때 프로그램은 종료된다.

싱글쓰레드와 멀티쓰레드

하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후에 다른 작업을 시작하지만, 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드(th1, th2)가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는 것과 같이 느끼게 한다.

하나의 쓰레드로 두개의 작업을 수행한 시간과 두개의 쓰레드로 두 개의 작업을 수행한 시간은 거의 같다.
오히려 두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸리게 되는데 그 이유는 쓰레드간의 작업 전환(context switching)에 시간이 걸리기 때문이다.

그래서 싱글 코어에서 단순히 CPU만을 사용하는 계산작업이라면 오히려 멀티쓰레드보다 싱 글쓰레드로 프로그래밍하는 것이 더 효율적이다.

쓰레드 I/O블락킹(blocking)

두 쓰레드가 서로 다른 지원을 사용하는 작업의 경우에는 싱글쓰레드 프로레스보다 멀티 쓰레드 프로세스가 더 효율적이다.
예를 들면 사용자로부터 데이커를 입력받는 직업, 네트워크로 파일을 주고 받는 직업, 프린터로 파일을 출력하는 작업과 같이 외부기기와의 입출력을 필요로 하는경우가 이에 해당한다.

쓰레드의 우선순위

작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업 시간을 갖게 할 수 있다.

쓰레드의 우선순위 지정하기
void setPriority(int newPriority) 쓰레드의 우선순위를 지정한 값으로 변경한다.
int getPriority() 쓰레드의 우선순위를 반환한다.
public static final int MAX PRIORITY = 10 //최대우선순위
public static final int MIN PRIORITY = 1 //최소우선순위
public static final int NORM PRIORITY = 5 //보통우선순위

쓰레드 그룹(thread group)

서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것
모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 한다.
쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 'main쓰레드 그룹'에 속한다.
자신을 생선한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속받는다.

Thread (ThreadGroup group, String name)
Thread (ThreadGroup group, Runnable target)
Thread (ThreadGroup group, Runnable target, String name)
Thread (ThreadGroup group, Runnable target, String name, long stacksize)

ThreadGroup getThreadGroup () 쓰레드 자신이 속한 쓰레드 그룹을 반환한다.
Void uncaughtException(Thread t, ThrowabLe e) 처리되지 않은 예외에 의해 쓰레드 그룹의 쓰레드가 실행이 종료되었을 때, 그VN에 의해 이 메서드가 자동적으로 호출된다.

쓰레드 그룹(thread group)의 메서드

데몬쓰레드(deamon thread)

-일반 쓰레드(none-deamon thread)의 작업을 돕는 보저적인 역활을 수행한다.
-일반 쓰레드가 모두 종료되면 자동적으로 종료된다.
-가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용된다.
-무한 루프와 조건문을 이용해서 실행 후 대기하다가 특정조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.

boolean isDaemon() -쓰레드가 데몬 쓰레드인지 확인하낟. 데몬쓰레드이면 true를 반환
void setDaemon(boolean on) -쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경 매개변수 on울 true로 지정하면 데몬 쓰레드가 된다.

*setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 한다. 그렇지 않으면 IllegalThreadStateException이 발생한다.

쓰레드의 상태

쓰레드 실행제어

-쓰레드의 실행을 제어할 수 있는 메서드가 제공된다.
이 들을 활용해서 효율적인 프로그램의 작성할 수 있다.

sleep()

-현재 쓰레드를 지정된 시간동안 멈추게 한다.

staic void sleep(long millis)	// 천분의 일초 단위
staic void sleep(long millis, int nanos)	//천분의 일초 + 나노초

-항상 try-catch문으로 예외처리를 해야 한다.(InterruptedException이 발생하면 깨어남)

try {
	Thread.sleep(1, 500000);	// 쓰레드를 0.0015초 동안 멈추게 한다.
} catch(InterruptedException e) {}     

-특정 쓰레드를 지정해서 멈추게 하는것은 불가능하다.

interrupt()

-대기상태(WAITING)인 쓰레드를 실행대기 상태(RUNNABLE)로 만든다.

void interrupt() 쓰레드의 interrupted상태를 false에서 true로 변경
boolean isInterrupted() 쓰레드의 interrupted상태를 반환
static boolean isInterrupted() 현제 쓰레드의 interrupted상태를 알려주고, false로 초기화

suspend(), resume(), stop()

-쓰레드의 실행을 일시정지, 재개, 완전정지 시킨다.

void suspend() 쓰레드를 일시정지 시킨다.
void resume() suspend()에 의해 일시정지된 쓰레드를 실행대기 상태로 만든다.
void stop() 쓰레드를 즉시 종료시킨다.

-suspend(), resume(), stop()은 교착상태(deadlock)를 일으키기 쉽게 작성되었기 때문에 모두 'deprecated'되었다.

join()과 yield()

join() -다른 쓰레드의 작업을 기다린다.

-지정된 시간동안 특정 쓰레드가 작업하는 것을 기다린다.

void join() //작업이 모두 끝날 때까지
void join(long millis) // 천분의 일초 동안
void join(long millis, int nanos) // 천분의 일초 + 나노초 동안

-예외처리를 해야 한다.(InterruptedException이 발생하면 작업 재개)

yield() -다른 쓰레드에게 양보한다.

-남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행대기한다.
-yield()과 interrupt()를 적절히 사용하면, 응답성과 효울을 높일 수 있다.

쓰레드의 동기화(synchronization)

-멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다.
-진행중인 작업이 다른 쓰레드에게 간섭받지 않게 하려면'동기화'가 필요

쓰레드의 동기화 - 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는것

-동기화하려면 간섭 받지 않아야 하는 문장들은 '임계영역'으로 성정해야 한다.
-임계영역은 락(lock)을 얻은 단 하나의 쓰레드만 출입가능(객체1개에 락1개)

synchronized를 이용한 동기화

-synchronized로 임계영역(lock이 걸리는 영역)을 설정하는 방법2가지

wait()과 notify()

-동기화의 효율을 높이기 위해 wait(), notify()를 사용한다.

wait(), notify(), notifyAll()
-Object에 정의 되어있다.
-동기화 블록(synchronized블록) 내에서만 사용할 수 있다.
-보다 효율적인 동기화를 가능하게 한다.

profile
오늘의 기록

0개의 댓글