1
예외처리와 스레드
[ 단원 08 ]
단원 08 예외처리와 스레드
학습목표
❖ 예외와 예외처리를 이해하고 프로그래밍에 활용할 수 있다.
▪
• 에러와 예외의 차이와 클래스 계층구조▪
• 체크 예외와 비체크 예외의 차이와 처리 방법▪
• 새로운 예외 클래스의 생성과 사용 방법❖ 스레드를 이해하고 스레드를 프로그래밍에 활용할 수 있다.
▪
• 다중 작업과 스레드의 이해▪
• 클래스 Thread를 상속받아 처리하는 스레드 구현▪
• 인터페이스 Runnable을 구현하여 처리하는 스레드 구현❖ 스레드 상태와 우선순위, 동기화를 이해하고 프로그래밍에 활용할 수 있다.
▪
• 스레드 상태와 전이 방법▪
• 스레드 우선순위의 지정 방법▪
• 스레드에서 자원의 공유 문제와 동기화 처리▪
• Object의 wait( )와 notify( )를 사용한 동기화3
단원 08 예외처리와 스레드
4
1. 예외처리 개요
단원 08 예외처리와 스레드
예외와 에러
❖ 자바에서 오류: 에러(error)와 예외(exception)로 구별
▪
예외•
자바 프로그램에서 실행 중에 발생할 수 있는 경미한 오류를 예외•
적절한 처리 모듈을 추가하여 발생한 문제를 복구 가능•
IndexOutOfBoundsException과 같이 ◯ ◯ ◯ Exception 형태▪
에러•
메모리나 내부의 심각한 문제•
복구가 불가능한 오류인 OutOfMemoryError, InternalError 등▪
에러와 예외를 모두 객체로 만들어 처리, 관련 클래스 계층 구조•
클래스 Throwable– 하부로 예외인 Exception 클래스와 에러인 Error 클래스
•
Exception 클래스 하부– 다양한 예외를 위한 클래스
5
Section 1 예외처리 개요 p302
단원 08 예외처리와 스레드
다양한 예외의 발생
❖ 예외가 발생하면
▪
바로 프로그램이 중단되므로 발생한 이후의 프로그램을 실행되지 않음▪
발생한 예외 클래스의 이름과 예외가 발생한 프로그램 소스와 줄 번호가 표시▪
NullPointerException•
실제 변수에 저장된 객체가 null임에도 불구하고 객체의 멤버를 참조하려는 경우 발생▪
ArrayIndexOutOfBoundsException•
배열에서 배열의 첨자 범위를 벗어난 첨자 사용▪
ArithmeticException,•
0으로 수를 나누려 할 때 발생▪
ArrayStoreException•
배열에 잘못된 유형의 객체를 저장하려 할 때 발생▪
ClassCastException•
객체를 변환할 수 없는 유형으로 변환하려고 할 때 발생▪
NegativeArraySizeException•
배열의 크기를 음수로 지정하는 경우 발생6
Section 1 예외처리 개요
단원 08 예외처리와 스레드
실습예제
❖ 8-1, 8-2
7
Section 1 예외처리 개요
단원 08 예외처리와 스레드
예외처리 구문 try
❖ 예외처리(exception handling)
▪
실행 중에 여러 이유로 예상하지 못했던 문제가 발생한 경우 이를 적절히 처리하는 모듈❖ 예외처리 모듈
▪
try ~ catch ~ finally 문장•
예외 발생과 상관없이 finally의 블록은 실행•
catch와 finally 둘 중 하나는 옵션8
Section 1 예외처리 개요
단원 08 예외처리와 스레드
실습예제
❖ 8-3
▪
try 내부에서 예외가 발생하면•
더 이상 try 블록 내부의 나머지 문장은 실행하지 않음9
Section 1 예외처리 개요
단원 08 예외처리와 스레드
catch 처리 순서
❖ 여러 개의 catch 문을 이용하는 경우
▪
프로그램이 실행되는 순간에 예외가 발생하면▪
여러 개의 catch 문 중에서 위에서부터 순차적으로 catch(ExceptionType var) 문 에서 ExceptionType 인자 유형을 검사•
발생된 예외의 유형과 일치하거나 하위 클래스이면 먼저 만나는 catch 문 블록만을 실행▪
주의할 점•
여러 개의 catch 구문에 기술하는 ExceptionType이 하위 클래스인것부터 먼저 catch 블록을 기술
10
Section 1 예외처리 개요
단원 08 예외처리와 스레드
실습예제 8-4
11
Section 1 예외처리 개요
❖
예외 참조 변수인 e를 바로 출력하면 예외 클래스 이름과 메시지가 출력❖
발생한 예외의 메시지만 알아보려면 e.getMessage() 메소드를 사용❖
예외 처리를 하지 않은 경우 출력되는 모든 메시지를 보려면 e.printStackTrace() 메 소드를 사용❖
모든 예외 클래스는 Exception의 하위 클래스이므로 Exception 유형 의 참조변수 catch 블록은 모든 종류의 예외를 처리할 수 있으므로 반드 시 catch 블록의 마지막에 배치단원 08 예외처리와 스레드
12
2. 체크 예외와 예외 생성
단원 08 예외처리와 스레드
예외 계층 구조
❖ RuntimeException
▪
그 하부 예외▪
ArithmeticException, NullPointerException,ArrayIndexOutOfBoundsException, IndexOutOfBoundsException
13
Section 2 체크 예외와 예외 생성 p308
단원 08 예외처리와 스레드
비체크 예외와 체크 예외
❖ 비체크 예외(unchecked exception)
▪
RuntimeException과 그 하부 예외가 발생할 가능성이 있는 코드들은 try ~ catch 문이 선택적•
RuntimeException, ArithmeticException, NullPointerException❖ 체크 예외(checked exception)
▪
예외 중에서 RuntimeException과 그 하부 예외를 제외한 모든 예외•
FileNotFoundException– 존재하지 않는 파일을 처리
•
ClassNotFoundException– 사용하려는 클래스의 이름을 잘못 기술
•
DataFormatException– 입력한 데이터의 형식이 잘못
14
Section 2 체크 예외와 예외 생성
단원 08 예외처리와 스레드
체크 예외
❖ 클래스 Class
▪
클래스 Object의 하위 클래스로 응용 프로그램에서 실행 중인 클래스나 인터페이 스를 대표하는 클래스▪
Class의 메소드 중에 정적 메소드인 forName(className)•
인자인 className의 객체를 반환하는 메소드•
인자인 className의 클래스가 없다면– ClassNotFoundException 예외를 발생
•
forName(className)을 호출하는 부분에서 반드시 예외처리를 수행– 아니면 컴파일 에러 발생
15
Section 2 체크 예외와 예외 생성
단원 08 예외처리와 스레드
실습예제
❖ 8-5
❖ 메소드의 예외 확인
•
메소드 선언을 살펴보면 발생할 수 있는 예외를 알 수 있음•
발생하는 예외가 체크 예외라면 메소드 호출 부분에서 예외처리가 없으면– 컴파일 시간에 문법 오류가 발생
16
Section 2 체크 예외와 예외 생성
단원 08 예외처리와 스레드
체크 예외의 2가지 처리방법
❖ 첫 번째 방법
▪
try~catch 구문 사용 방법17
Section 2 체크 예외와 예외 생성
public class TryCheckedException {
public static void main(String[] args) {
//메소드 Class.forName()을 사용하려면 반드시 예외처리를 해야 함 try {
System.out.println(Class.forName("java.lang.Object"));
} catch (ClassNotFoundException ex) { System.out.println(ex);
} } }
단원 08 예외처리와 스레드
18
❖ 두 번째 방법
▪
예외가 발생될 수 있는 구문이 속한 메소드에서 다시 예외를 전달(propagation) 하는 방법▪
상위 메소드로 예외처리를 미루는 방법public class PropagateCheckedException {
//메소드 선언에서 다시 예외 ClassNotFoundException의 발생을 전달
public static void main(String[] args) throws ClassNotFoundException {
//메소드 Class.forName()을 사용하려면 반드시 예외처리를 해야 함System.out.println(Class.forName("java.lang.Object"));
} }
단원 08 예외처리와 스레드
예외 클래스 생성과 발생
❖ 새로운 예외 클래스 정의
▪
Exception 을 상속받아 구현▪
생성자에서 super(msg)로 구현▪
msg에 저장된 문자열은 메소드 getMessage()에 의해 반환❖ 생성된 예외의 발생
▪
생성된 예외 클래스는 필요한 경우 예외를 발생(throws) 가능•
예외를 발생시키는 부분에서는 new MyException("내가 만든 예외")▪
메소드 선언에서 throws MyException을 기술•
발생되는 예외가 여러 개라면 여러 예외 유형을 쉼표로 구분하여 기술19
Section 2 체크 예외와 예외 생성
단원 08 예외처리와 스레드
실습예제 : 새로운 예외의 처리
❖ 8-9
▪
super(msg)를 구현하지 않았다면•
getMessage()는 null을 반환20
Section 2 체크 예외와 예외 생성
단원 08 예외처리와 스레드
21
3. 스레드 개요
단원 08 예외처리와 스레드
스레드(thread)
❖ 프로그램 내에서 실행되는 프로그램 제어 흐름
▪
프로그램의 내부의 실행 흐름인 스레드를 여러 개 만들 수 있다면•
여러 일을 동시에 처리하는 듯한 느낌22
Section 3 스레드 개요 p314
단원 08 예외처리와 스레드
다중 작업과 다중 스레드
❖ 다중 스레드(multi-thread) 프로그램
▪
여러 개의 스레드를 이용하는 프로그램❖ 스레드
▪
가벼운 프로세스(light-process)▪
장점•
하나의 프로그램 내부에서 실행되는 스레드 는 프로세스보다 오버헤드가 적으면서 처리 할 작업을 동시에 실행23
Section 3 스레드 개요
❖ 다중 작업(multi-tasking)
▪
여러 프로그램을 동시에 실행시켜 서 로 이동하면서 작업이 가능❖ 프로세스(process) 란?
▪
실행되고 있는 프로그램▪
프로세스마다 고유한 저장공간을 사용 하며 독립적으로 실행▪
단점•
여러 프로세스를 실행하려면 프로세스 간의 정보 교환에 많은 시간이 소요단원 08 예외처리와 스레드
스레드를 처리하는 첫 번째 방법
❖ 클래스 Thread를 상속받아 구현하는 방법
▪
클래스 Thread를 상속받아 새로운 스 레드를 정의▪
Thread의 메소드 run()에서 스레드 작업을 재정의▪
단점•
다른 클래스를 상속받아야 하는 경우 이용할 수 없음▪
실행 방법•
객체를 생성한 후 메소드 start()를 호 출하여 스레드를 시작– 메소드 start()의 호출에 의해 스레드 에 재정의된 run()이 수행
24
Section 3 스레드 개요
단원 08 예외처리와 스레드
스레드의 주요 메소드
25
Section 3 스레드 개요
단원 08 예외처리와 스레드
실습예제 8-10
❖ 1 에서 9까지 출력하는 스레드
26
Section 3 스레드 개요
SimpleThread.java
단원 08 예외처리와 스레드
스레드 이름 지정과 반환
❖ 이름을 가진 스레드를 생성
▪
메소드 setName()을 사용❖ 지정된 스레드의 이름을 반환
▪
메소드 getName()을 사용❖ 스레드를 잠시 멈추게 하려면
▪
sleep(시간)을 이용, 지정된 인자는 천분의 1초27
Section 3 스레드 개요
단원 08 예외처리와 스레드
실습예제 8-11
28
Section 3 스레드 개요
class IncThread extends Thread {
//생성자 구현public IncThread(String name) {
setName(name); //생성자 이름 지정 }public void run() {
for (int i = 1; i < 5; i++) { try {
sleep(50); // 50/1000 초 대기
System.out.print(getName() + ": " + i);
System.out.println(", 활성화된 스레드 수: " + activeCount());
} catch (Exception e) { e.printStackTrace();
} } } }
class DecThread extends Thread { public void run() {
for (int i = 5; i > 1; i--) try {
sleep(50); // 50/1000 초 대기
, 2000(2초)으로 수정가능System.out.print(getName() + ": " + i);
System.out.println(", 활성화된 스레드 수: " + activeCount());
} catch (Exception e) { e.printStackTrace();
} }
}
단원 08 예외처리와 스레드
29
public class ThreadTest {
public static void main(String[] args) {
IncThread inc = new IncThread("증가 스레드");
inc.start();
DecThread dec = new DecThread();
dec.start();
}
}
단원 08 예외처리와 스레드
인터페이스 Runnable
❖ 인터페이스 Runnable
▪
추상 메소드 run()으로 구성된 단순한 인터페이스▪
스레드로 실행하려는 클래스•
인터페이스 Runnable을 상속받아 메소드 run()에서 스레드 기능을 구현❖ 클래스 Thread
▪
Thread는 인터페이스 Runnable을 상속받아 메소드 run()을 재정의30
Section 3 스레드 개요
package java.lang;
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
…}
단원 08 예외처리와 스레드
스레드를 처리하는 두 번째 방법
❖ 인터페이스 Runnable의 run( ) 메소드 구현
▪
스레드의 이름 반환•
Thread.currentThread().getName()을 호출❖ 스레드를 시작
▪
구현한 스레드의 객체 인자로 Thread를 생성•
스레드 시작 메소드 start()를 호출31
Section 3 스레드 개요
단원 08 예외처리와 스레드
실습예제 8-12
32
Section 3 스레드 개요
단원 08 예외처리와 스레드
33
4. 스레드 상태와 우선순위
단원 08 예외처리와 스레드
스레드 상태 6가지
❖ 스레드의 상태
▪
클래스 Thread 내부에 enum State으로 선언•
스레드는 객체가 생성되면 NEW라는 상태•
start()가 호출되면 RUNNABLE 상태로 이동34
Section 4 스레드 상태와 우선순위 p322
단원 08 예외처리와 스레드
스레드 상태 전이
❖ NEW : 스레드는 객체가 생성되면 이동되는 상태
❖ RUNNABLE : 메소드 start()가 호출되면 이동
❖ TERMINATED : 스레드가 완전히 종료된 상태
▪
더 이상 NEW 또는 RUNNABLE 등의 다른 상태로 전이가 불가능한 상태❖ BLOCKED, WAITING, TIMED_WEIGHTING
35
Section 4 스레드 상태와 우선순위
단원 08 예외처리와 스레드
스레드 우선순위
❖ setPriority()
▪
스레드의 우선순위 지정❖ getPriority()
▪
현재 값을 반환❖ 스레드의 우선순위
▪
1에서 10까지 지정 가능36
Section 4 스레드 상태와 우선순위
단원 08 예외처리와 스레드
실습예제 8-13
❖ 스레드 우선순위는 특별히 지정하지 않으면
▪
상수 NORM_PRIORITY의 값인 537
Section 4 스레드 상태와 우선순위
public class ThreadState implements Runnable { public void run() {
for (int i = 1; i < 10; i++) { try {
Thread.sleep(40);
System.out.print(Thread.currentThread().getState() + ",
");
System.out.println(Thread.currentThread().getName() + ":
" + i);
} catch (InterruptedException e) {
System.err.pprintln("InterruptedException이 발생되어 스레드를 종료합니다. ");
return;
} catch (Exception e) { e.printStackTrace();
} } }
public static void main(String[] args) throws InterruptedException {
System.out.println("스레드의 모든 상태: 6 가지");
for (Thread.State c : Thread.State.values()) System.out.print(c + " ");
System.out.println('\n');
Thread th = new Thread(new ThreadState());
System.out.println("기본 우선순위: " +
th.getPriority());
단원 08 예외처리와 스레드
38
5. 스레드 동기화
단원 08 예외처리와 스레드
다중 스레드의 문제
❖ 다중 스레드 상에서 공유 자원의 처리 문제
▪
예상하지 못한 문제 발생 가능성 존재•
스레드 A가 공유 자료를 처리하는 도중에•
다른 스레드 B가 그 자료를 처리한다면– 다중 스레드에서 수행해야 할 작업이 하나의 단위로 처리되지 않아서 발생할 수 있는 문제
39
Section 5 스레드 동기화 p326
단원 08 예외처리와 스레드
다중 스레드 구현
❖ 은행 계좌 모의 구현
▪
신청한 금액을 인출하는 메소드 withdraw()▪
신청한 금액을 입금하는 메소드 deposit()❖ 인터페이스 Runnable을 상속받는 스레드
▪
클래스 SyncTest는 은행계좌 객체 act 하나를 사용하여 20000원 이하의 금액에 대하여•
입금(deposit)과 출금(withdraw)을 반복 처리▪
withdraw()는 어떠한 경우에도 잔고가 음수가 발생하지 않도록 의도•
만일 계좌의 잔고가 0원 미만이면 계좌에 문제가 발생한 것이므로 스레드를 종료40
Section 5 스레드 동기화
public class SyncTest implements Runnable { BankAccount act = new BankAccount();
public void run() { while (true) {
int amount = new Random().nextInt(10000);
act.deposit(amount);
act.withdraw(amount*2);
if (act.balance < 0) {
System.out.printf("[%s] ", Thread.currentThread().getName());
System.out.println("잔고: " + act.balance + " => 오류 종료");
return;
} } }
…
}
단원 08 예외처리와 스레드
다중 스레드의 임계 영역
❖ 임계영역(critical section)
▪
다중 스레드에서 하나의 스레드가 배타적(exclusive)으로 공유 자원을 독점하도록 해야 하는 부분❖ 임계영역의 실례
▪
하나의 스레드가 은행계좌의 인출 메소드 withdraw() 기능을 수행•
중간에 다른 스레드가 들어와 일을 할 수 없도록 잠금(lock) 장치 필요•
인출 기능을 모두 수행했다면 다른 스레드가 일을 하도록 잠금 장치를 해지(unlock) 해 야 할 것▪
즉 다음 소스에서 1, 2, 3 세 개의 문장은 독점적인 실행 필요41
Section 5 스레드 동기화
단원 08 예외처리와 스레드
동기화
❖ 스레드 동기화(thread synchronization)
▪
다중 스레드에서 임계영역의 독점적 처리 해결 방법❖ 동기화 방법 크게 2가지
▪
메소드의 동기화•
메소드의 동기화는 메소드의 지정자인 키워드 synchronized를 기술▪
블록의 동기화•
블록의 동기화는 synchronized (Object) {…}– 여기서 Object는 잠금 장치를 수행하는 객체
42
Section 5 스레드 동기화
단원 08 예외처리와 스레드
은행계좌의 동기화 문제
❖ 임계 영역에서 자원의 공유로 인한 문제
▪
두 스레드 Thread-0와 Thread-1•
동기화가 구현되지 않은 메소드 withdraw()에서 발생하는 문제– 두 스레드가 각각 인출을 수행하므로 잔고가 음수가 되는 문제가 발생
43
Section 5 스레드 동기화
단원 08 예외처리와 스레드
44
Section 5 스레드 동기화
package synchronize;
import java.util.Random;
class BankAccount { int balance = 0;
//메소드 전체의 동기화 처리
public synchronized void withdraw(int money) { if (money > 0 && balance >= money) {
balance -= money;
System.out.printf("%d 인출하여 현재잔고 %d입니다. %n", money, balance);
} else if (balance < money)
System.out.println("잔고가 부족하여 인출할 수 없습니다.");
}
//블록 동기화로 메소드 전체의 동기화 처리
public void deposit(int money) {
synchronized (this) { if (money > 0) {
balance += money;
System.out.printf("%d 입금하여 현재잔고 %d입니다. %n", money, balance);
} } } }
단원 08 예외처리와 스레드
45
public class SyncTest implements Runnable {
BankAccount act = new BankAccount();
public void run() {
//while (true) {for (int i = 0; i < 3; i++) {
int amount = new Random().nextInt(10000);
amount = amount % 10 * 1000;
System.out.printf("[%s]
금액=%d %n"
, Thread.currentThread().getName(), amount);act.deposit(amount);
act.withdraw(amount * 2);
if (act.balance < 0) {
System.out.printf("[%s] ", Thread.currentThread().getName());
System.out.println("잔고: " + act.balance + " => 오류 종료");
return;
} } }
public static void main(String[] args) {
Runnable r = new SyncTest();new Thread(r).start();
new Thread(r).start();
} }
단원 08 예외처리와 스레드
메소드 wait( )와 notify( ), notifyAll( )의 이해
❖ Object 의 메소드 wait()와 notify(), notifyAll()을 사용
▪
스레드 동기화에서 동기화 효율을 높이는 방안▪
메소드 wait()•
동기화 블록에서 객체의 특정한 작업을 위해 처리 중인 스레드를 WAITING 또는 TIMED_WAITING 상태로 이동▪
notify() 또는 notifyAll()•
다시 스레드 상태를 RUNNABLE로 이동시켜 스레드 작업을 다시 수행 시킴▪
특징•
Object 클래스의 메소드이므로 모든 객체에서 사용 가능•
키워드 synchronized를 사용하는 동기화 내부에서만 사용 가능46
Section 5 스레드 동기화
단원 08 예외처리와 스레드
메소드 wait( )와 notify( ), notifyAll( )의 사용
❖ 메소드 wait()
❖ notify() 또는 notifyAll()
47
Section 5 스레드 동기화
단원 08 예외처리와 스레드
48
Section 5 스레드 동기화
package wait;
import java.util.Random;
class BankAccount { intbalance = 0;
intdiff = 0;
publicsynchronizedvoid withdraw(int money) { if (money < 0) {
System.out.println("인출 금액이 잘못됐습니다.");
return;
}int count = 0;
while (balance < money) {
System.out.printf("[%s] ", Thread.currentThread().getName());
// 지속적으로 잔금이 부족하여 메소드 종료 if (++count > 3) {
System.out.println("잔액이 부족하여 출금처리 못하고 종료합니다.");
return;
}
System.out.printf("%16s", "wait(1000) 호출: ");
System.out.printf("인출요구금액=%6d, balance=%6d %n", money, balance);
try {
// wait();
wait(1000);
}catch (InterruptedException e) { System.err.println(e);
} }
System.out.printf("[%s] ", Thread.currentThread().getName());
balance -= money;
System.out.printf(" %12s 정상인출금액=%6d, balance=%6d %n", "정상 출금처리:", money,balance);
}
publicsynchronizedvoid deposit(int money) { if (money < 0) {
System.out.println("입금 금액이 잘못됐습니다.");
return;
}balance += money;
System.out.printf("[%s] ", Thread.currentThread().getName());
System.out.printf("%16s", "notify() 호출: ");
System.out.printf("계좌입금금액=%6d, balance=%6d %n", money, balance);
// notify();
notifyAll();
} }
단원 08 예외처리와 스레드
49
publicclass SyncTest implements Runnable { BankAccountact = new BankAccount();
publicvoid run() {
for (int i = 1; i < 3; i++) {
int amount = (int) (new Random().nextDouble() * 5 + 1) * 10000;
act.deposit(amount);
amount = (int) (new Random().nextDouble() * 5 + 1) * 10000;
act.withdraw(amount);
} }
publicstaticvoid main(String[] args) { Runnable r = new SyncTest();
new Thread(r).start();
new Thread(r).start();
} }