Continuous Challenge

[Java의 정석_연습문제 풀이] Chapter13 쓰레드 본문

Study/Java의 정석

[Java의 정석_연습문제 풀이] Chapter13 쓰레드

응굥 2019. 11. 6. 11:05
728x90
728x90


[13-1] 쓰레드를 구현하는 방법에는 Thread 클래스로부터 상속받는 것과 Runnable 인터페이스를 구현하는 것 두 가지가 있는데, 다음의 코드는 Thread클래스를 상속받아서 쓰레드를 구현한 것이다. 이 코드를 Runnable 인터페이스를 구현하도록 변경하시오.

class Exercise13_1 {

public static void main(String args[]) {

Thread1 th1 = new Thread1();

th1.start();

}

}


class Thread1 extends Thread {

public void run() {

for (int i = 0; i < 300; i++) {

System.out.print('-');

}

}

}

답 :

class Exercise13_1 {

public static void main(String args[]) {

Runnable run = new Thread1();

Thread th = new Thread(run);

th.start();

}

public static void main(String args[]){

Thread th1 = new Thread(new Thread1());


th1.start();

}

}


class Thread1 implements Runnable {

public void run() {

for (int i = 0; i < 300; i++) {

System.out.print('-');

}

}

}


[13-2] 다음 코드의 실행결과로 옳은 것은?

class Exercise13_2 {

public static void main(String[] args) {

Thread2 t1 = new Thread2();

t1.run();


for (int i = 0; i < 10; i++)

System.out.print(i);

}

}


class Thread2 extends Thread {

public void run() {

for (int i = 0; i < 10; i++)

System.out.print(i);

}

}

a. 01021233454567689789처럼 0부터 9까지의 숫자가 섞여서 출력된다.

b. 012345678901234567890처럼 0부터 9까지의 숫자가 순서대로 출력된다.

c. IllegalThreadStateException이 발생한다.


답 : a b

해설 : Thread2클래스의 인스턴스를 생성하긴 했지만, start( )가 아닌 run( )을 호출함으로써 쓰레드를 실행시킨 것이 아니라 단순히 Thread2클래스의 메서드를 호출한 셈이 되었다. 만일 run( )이 아닌 start( )를 호출하였다면, 숫자가 섞여서 출력되었을 것이다.


[13-3] 다음 중 쓰레드를 일시정지 상태(WAITING)로 만드는 것이 아닌 것은?(모두 고르시오)

a. suspend( )

b. resume( )

c. join( )

d. sleep( ) 

e. wait( )

f. notify( )

답 : b, f

해설 : resume( )은 suspend( )의 호출로 인해 일시정지 상태가 된 쓰레드를 실행대기상태로 바꿔준다. notify( )역시 wait( )의 호출로 인해 일시정지 상태가 된 쓰레드를 다시 실행대기 상태로 바꿔준다. join( )은 현재 실행 중인 쓰레드를 멈추고 다른 쓰레드가 실행되도록 한다. 


[13-4] 다음 중 interrupt( )에 의해서 실행대기 상태(RUNNABLE)가 되지 않는 경우는? (모두 고르시오)

a. sleep( )에 의해서 일시정지 상태인 쓰레드

b. join( )에 의해서 일시정지 상태인 쓰레드

c. wait( )에 의해서 일시정지 상태인 쓰레드

d. suspend( )에 의해서 일시정지 상태인 쓰레드

답 : a, b d

해설 : suspend( )를 제외한 나머지 메서드들은 interrupt( )가 호출되면 InterruptedException이 발생하여 일시정지 상태에서 벗어나 실행대기 상태가 된다. (try-catch문으로 InterruptException을 처리해주어야 한다.)


[13-5] 다음의 코드를 실행한 결과를 예측하고, 직접 실행한 결과와 비교하라. 만일 예측한 결과와 실행한 결과의 차이가 있다면 그 이유를 설명하라.

class Exercise13_5 {

public static void main(String[] args) throws Exception {

Thread3 th1 = new Thread3();

th1.start();


try {

Thread.sleep(5 * 1000);

} catch (Exception e) {

}

throw new Exception("꽝 ~!!!");

}

}


class Thread3 extends Thread {

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println(i);

try {

Thread.sleep(1000);

} catch (Exception e) {

}

}

}

}

답 : 0123456789 가 출력되고 6초 후 프로그램이 종료된다.

직접 실행한 결과 : 0123456789가 출력되던 도중, Exception이 발생하여 "꽝~!!!"이 출력된다.

[실행결과]

0

1

2

3

4

5

Exception in thread "main" java.lang.Exception: 꽝~!!!

at Exercise13_5.main(Exercise13_5.java:12)

6

7

8

9

해설 : main과 th1 두 개의 쓰레드는 별도의 호출스택(call stack)에서 실행된다. 그래서 main에서 예외가 발생하여 종료되고 호출스택이 없어져도, 쓰레드 th1이 실행되는 호출스택에는 아무런 영향을 미치지 못한다.


[13-6] 다음의 코드를 실행한 결과를 예측하고, 직접 실행한 결과와 비교하라. 만일 예측한 결과와 실행한 결과의 차이가 있다면 그 이유를 설명하라.

class Exercise13_6 {

public static void main(String[] args) throws Exception {

Thread4 th1 = new Thread4();

th1.setDaemon(true); //쓰레드 th1을 데몬쓰레드로 설정한다.

th1.start();

try {

th1.sleep(5 * 1000);

} catch (Exception e) {

}

throw new Exception("꽝~!!!");

}

}


class Thread4 extends Thread {

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println(i);

try {

Thread.sleep(1000);

} catch (Exception e) {

}

}

}

}

직접 실행한 결과 : 01234 까지 출력된 후, Exception이 발생하여 "꽝~!!!"이 출력된 후 프로그램이 종료되었다.

[실행결과]

0

1

2

3

4

5Exception in thread "main"

java.lang.Exception: 꽝~!!!

at Exercise13_6.main(Exercise13_6.java:13)

해설 : 문제13-6에 'th1.setDaemon(true):' 한 문장을 추가해서 쓰레드 th1을 데몬 쓰레드(daemon thread)로 설정하였다. 데몬 쓰레드는 일반 쓰레드(데몬 쓰레드가 아닌 쓰레드)가 모두 종료되면 자동 종료되므로, main쓰레드(일반 쓰레드)가 종료됨과 동시에 자동 종료된다. 그래서 문제13-6과는 달리 쓰레드 th1이 main메서드의 종료와 동시에 종료되었다.


[13-7] 다음의 코드는 쓰레드 th1을 생성해서 실행시킨 다음 6초 후에 정지시키는 코드이다. 그러나 실제로 실행시켜보면 쓰레드를 정지시킨 다음에도 몇 초가 지난 후에서야 멈춘다. 그 이유를 설명하고, 쓰레드를 정지시키면 지체없이 바로 정지되도록 코드를 개선하시오. 

class Exercise13_7 {

static boolean stopped = false;


public static void main(String[] args) { 

Thread5 th1=new Thread5(); 

th1.start(); 

try{  

Thread.sleep(6*1000); 

}catch(Exception e){} 

stopped=true; // 쓰레드를 정지시킨다. 

th1.interrupt(); //일시정지 상태에 있는 쓰레드를 깨운다.

System.out.println("stopped"); 

}

}


class Thread5 extends Thread {

public void run() {

// Exercise13_7.stopped의 값이 false인 동안 반복한다.

for (int i = 0; !Exercise13_7.stopped; i++) {

System.out.println(i);

try {

Thread.sleep(3 * 1000);

} catch (Exception e) {}

}

}

}


[실행결과]

0

1

2

stopped

답 : Exercise13_7.stopped의 값이 바뀌어도 for문내의 Thread.sleep(3*100); 문장에 의해 일시정지 상태에 있는 경우, 시간이 지나서 일시정지 상태를 벗어날 때까지 for문을 벗어날 수 없기 때문에 이런 현상이 발생한다. 그래서 interrupt( )를 호출해서 자고 있는 (sleep( )에 의해 일시정지 상태에 있는) 쓰레드를 깨워야 즉시 정지하게 된다.


해설 : 쓰레드 th1은 아래의 반복문을 수행하다가 main메서드에서 Exercise13_7.stopped의 값을 true로 바꾸면 반복문을 빠져나와 수행을 종료하게 된다. 반복문 안에 쓰레드를 3초 동안 일시정지 상태로 하는 'Thread.sleep(3*1000)'이 있기 때문에 Exercise13_7.stopped의 값이 바뀌었다 하더라도 일시정지 상태에 있다면, 일시정지 상태가 끝나야만 반복문을 빠져나오게 된다.


그래서 쓰레드의 실행을 바로 종료시키려면 Exercise13_7.stopped의 값을 true로 바꾸는 것만으로는 부족하다. 그 외에 다른 방법이 더 필요하다. 그것은 바로 interrupt( )를 호출하는 것이다.

interrupt( )는 InterruptedException을 발생시킴으로써 Thread.sleep( )에 의해 일시정지 상태에 있던 쓰레드를 즉시 깨운다. 그래서 Exercise13_7.stopped의 값을 true로 바꾸고, interrupt( )를 호출하면 지연없이 즉시 쓰레드를 멈추게 할 수 있다.


[13-8] 다음의 코드는 텍스트기반의 타자연습게임인데 WordGenerator라는 쓰레드가 Vector에 2초마다 단어를 하나씩 추가하고, 사용자가 단어를 입력하면 Vector에서 일치하는 단어를 삭제하도록 되어 있다. WordGenerator의 run( )을 완성하시오.

import java.util.*;


class Exercise13_8 {

Vector words = new Vector();

String[] data = { "태연", "유리", "윤아", "효연", "수영", "서현", "티파니", "써니", "제시카" };


int interval = 2 * 1000; // 2초


WordGenerator wg = new WordGenerator();


public static void main(String args[]) {

Exercise13_9 game = new Exercise13_9();


game.wg.start();


Vector words = game.words;


while (true) {

System.out.println(words);


String prompt = ">>";

System.out.print(prompt);


// 화면으로부터 라인단위로 입력받는다.

Scanner s = new Scanner(System.in);

String input = s.nextLine().trim();


int index = words.indexOf(input);


if (index != -1) {

words.remove(index);

}

}

}


class WordGenerator extends Thread {

public void run() {

/*

* (1) 아래의 로직에 맞게 코드를 작성하시오. 

* 1.interval(2초)마다 배열 data의 값 중 하나를 임의로 선택해서 

* 2.words에 저장한다.

*/

}

}

}


[실행결과]

[] 

>>  

[서현

>> 서현  

[수영,윤아] 

>> 수영 

[윤아,유리] 

>> 유리 

[윤아,티파니] 

>> 티파니

[윤아,윤아,유리] 

>> 윤아

[윤아,유리]

>> 유리

[윤아,효연] 

>> 효연 

[윤아,티파니] 

>> 윤아

[티파니,윤아] 

>> 티파니

[윤아,수영,써니] 

>>

답 :

while(true) {

int rand = (int)(Math.random()*data.length);

words.add(data[rand]);

try {

Thread.sleep(interval);

} catch(Exception e) { }

}


[13-9] 다음은 사용자의 입력을 출력하고 종료하는 프로그램을 작성한 것으로, 10초 동안 입력이 없으면 자동종료되어야 한다. 그러나 실행결과를 보면, 사용자의 입력이 10초 안에 이루어졌음에도 불구하고 프로그램이 즉시 종료되지 않는다. 사용자로부터 입력받는 즉시 프로그램이 종료되도록 수정하시오.

import javax.swing.JOptionPane;


class Exercise13_9 {

public static void main(String[] args) throws Exception {

Exercise13_9_1 th1 = new Exercise13_9_1();

th1.start();


String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");

System.out.println("입력하신 값은 " + input + "입니다 .");

th1.interrupt(); // 쓰레드에게 작업을 멈추라고 요청한다.

}

}


class Exercise13_9_1 extends Thread {

public void run() {

int i = 10;


while (i != 0 && !isInterrupted()) {

System.out.println(i--);


try {

Thread.sleep(1000); // 1초 지연

} catch (InterruptedException e) {

interrupt();

}

}

System.out.println("카운트가 종료되었습니다.");

}

}

해설 : sleep( )에 의해 쓰레드가 잠시 멈춰있을 때, interrupt()를 호출하면 InterruptedException이 발생되고 쓰레드의 interrupted상태는 false로 자동 초기화된다.

728x90
728x90
Comments