LostCatBox

Java 배우기 CH13

Word count: 946Reading time: 5 min
2022/12/23 15 Share

쓰레드

프로세스와 쓰레드

  • 프로세스: 실행 중인 프로그램, 자원(resources)과 쓰레드로 구성
  • 쓰레드: 프로세스 내에서 실제 작업을 수행
    모든 프로세스는 최소한 하나의 쓰레드를 가지고있다.
  • 하나의 새로운 프로세스를 생성하는 거보다 하나의 새로운 쓰레드를 생성해주는 것이 더 적은 비용이 든다

Untitled

Untitled

  • 단점
    • 동기화
    • 교착상태
    • 기아(실행할기회없음)

쓰레드의 구현과 실행

  • 자바는 단일 상속! 따라서 인터페이스 구현이 더.. 좋을수도
  • 클래스 상속받기
  • 인터페이스 구현

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Ex13_1 {
public static void main(String args[]) {
ThreadEx1_1 t1 = new ThreadEx1_1();

Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r); // 생성자 Thread(Runnable target) //run()이 구현되어있는 인터페이스 받

t1.start();
t2.start();
}
}

class ThreadEx1_1 extends Thread {
public void run() {
for(int i=0; i < 5; i++) {
System.out.println(getName()); // 조상인 Thread의 getName()을 호출 // this.getName()이며, 상속받았음!!
}
}
}

class ThreadEx1_2 implements Runnable {
public void run() {
for(int i=0; i < 5; i++) {
// Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
System.out.println(Thread.currentThread().getName());
}
}
}

쓰레드의 실행 - start()

  • 쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다. 하지만 OS스케줄러가 실행 순서 결정하므로 순서 보장안됨. start()하자마자 바로 실행아님

  • t1.start()
    t2.start()

  • start()와 run()차이
    결국 start()을 해야 새로운 호출스택을 생성후 run()을 돌린다

Untitled

main쓰레드

  • main메서드의 코드를 수행하는 쓰레드
  • 쓰레드는 “사용자 쓰레드”와 “데몬 쓰레드” 두 종류가있다.
    데몬 쓰레드==보조 쓰레드
  • 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
  • 즉, main 쓰레드가 종료되어도 run 쓰레드가 실행중이므로 프로그램 종료안됨!!

Untitled

싱글쓰레드와 멀티쓰레드

  • 그림과 다르게 멀티쓰레드는 OS스케줄러에 따라 길이가 다를수있음.

Untitled

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

  • 외부장치의 입력 출력

Untitled

쓰레드의 우선순위(priority of thread)

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

Untitled

하지만 현실은 OS스케줄러에 반드시 희망사항대로 되지않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Ex13_6 {
public static void main(String args[]) {
ThreadEx6_1 th1 = new ThreadEx6_1();
ThreadEx6_2 th2 = new ThreadEx6_2();

th2.setPriority(7);

System.out.println("Priority of th1(-) : " + th1.getPriority());
System.out.println("Priority of th2(|) : " + th2.getPriority());
th1.start();
th2.start();
}
}

쓰레드 그룹

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

Untitled

쓰레드 그룹의 메서드

Untitled

데몬 쓰레드(daemon thread)

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

Untitled

  • setDaemon()은 반드시 start()호출하기 전에 실행되어야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Ex13_7 implements Runnable  {
static boolean autoSave = false;

public static void main(String[] args) {
Thread t = new Thread(new Ex13_7()); //Thread(Runnable r)Runnable인터페이스 받은 클래스
t.setDaemon(true); // 이 부분이 없으면 종료되지 않는다.
t.start();

for(int i=1; i <= 10; i++) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {}
System.out.println(i);

if(i==5) autoSave = true;
}

System.out.println("프로그램을 종료합니다.");
}

public void run() {
while(true) {
try {
Thread.sleep(3 * 1000); // 3초마다
} catch(InterruptedException e) {}

// autoSave의 값이 true이면 autoSave()를 호출한다.
if(autoSave) autoSave();
}
}

public void autoSave() {
System.out.println("작업파일이 자동저장되었습니다.");
}
}

쓰레드의 상태

Untitled

쓰레드의 실행제어

  • 쓰레드의 실행을 제어할 수 있는 메서드가 제공된다
  • 이 들을 활용해서 보다 효율적인 프로그램의 작성할 수 있다.
  • static 메서드는 자기 자신에게만 호출가능

Untitled

Tread 실행제어 static 메서드

  • sleep() 잠
  • yield() 양보

(static) sleep()

  • 현재 쓰레드를 지정한 시간동안 멈추게 한다. (this.sleep()임)
  • 예외처리를 해야한다.(InterruptedException이 발생하면서 깨어남) (깨어나는것은 time up 또는 interrupted밖에없다)
  • 특정 쓰레드를 지정해서 멈추게 하는 것은 불가능하다.

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Ex13_8 {
public static void main(String args[]) {
ThreadEx8_1 th1 = new ThreadEx8_1();
ThreadEx8_2 th2 = new ThreadEx8_2();
th1.start(); th2.start();

try {
th1.sleep(2000); //th1을2초동안 잠자는게아니라, main이 잠을잠. 실행시키는자가
//따라서 th1.sleep()아니라 Thread.sleep()이 올바른 표
} catch(InterruptedException e) {}

System.out.print("<<main 종료>>");
} // main
}

class ThreadEx8_1 extends Thread {
public void run() {
for(int i=0; i < 300; i++) System.out.print("-");
System.out.print("<<th1 종료>>");
} // run()
}

class ThreadEx8_2 extends Thread {
public void run() {
for(int i=0; i < 300; i++) System.out.print("|");
System.out.print("<<th2 종료>>");
} // run()
}

interrupt()

  • 대기상태(WAITING)인 쓰레드를 실행대기 상태(RUNNABLE)로 만든다
  • interrupt()는 해당 쓰레드클래스의 iv의 interrupted의 값을 변화시킴
  • static interrupted()는 static이므로 지금 돌아가는 것의 interrupted여부를 확인하는것임!!!!!

Untitled

suspend(), resume(), stop()

  • 쓰레드의 실행을 일시정지, 재개, 완전정지 시킨다.
  • 하지만 위의 함수보두 deprecated 이므로 권장 사용 안함!!

Untitled

Untitled

join()

  • 지정된 시간동안 특정 쓰레드가 작업하는 것을 기다린다
  • 예외처리를 해야한다.(InterruptedException이 발생하면 작업 재개)

Untitled

Untitled

yield()

  • 남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행대기한다.
  • yield()와 interrupt()를 적절히 사용하면, 응답성과 효율을 높일 수있다. >> yield()는 OS스케줄러에게 의견제시하는거임(반드시X)

Untitled

쓰레드의 동기화(synchronization)

  • 멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다.
  • 진행중인 작업이 다른 쓰레드에게 간섭받지 않게 하려면 동기화 필요(한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지못하도록함)
  • 동기화하려면 간섭받지 않아야하는 문장들을 “임계 영역”으로 설정
  • 임계영역은 락(lock)을 얻은 단 하나의 쓰레드만 출입가능(객체 1개에 락 1개)
  • 해당 객체와 관련된 곳은 모두 동기화해야함

synchronized를 이용한 동기화

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

  • 예제

Untitled

wait()와 notify()

  • 동기화의 효율을 높이기 위해 wait(), notify()를 사용
  • Object클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할수있다.
  • wait() - 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣는다
  • notify() - waiting pool에서 대기중인 쓰레드 중의 하나를 깨운다
  • notifyAll() - waiting pool에서 대기중인 모든 쓰레드를 깨운다
CATALOG
  1. 1. 쓰레드
  2. 2. 쓰레드의 동기화(synchronization)