이번 시간에는 스레드에 대해서 공부하겠습니다. 스레드란 어떠한 프로그램내에서 특히 프로세스 내에서 실행되는 흐름의 단위입니다.
1. Thread
- 메모리를 할당받아 실행 중인 프로그램을 프로세스라고 합니다.
- 프로세스 내의 명령어 블록으로 시작점과 종료점을 가진다.
- 실행중에 멈출 수 있으며 동시에 수행 가능하다.
- 어떠한 프로그램내에서 특히 프로세스 내에서 실행되는 흐름의 단위.
2. Thread 클래스
- JDK에서 지원하는 java.lang.Thread 제공
<Thread 생성자>
Thread() |
|
Thread(String s) |
스레드 이름 |
Thread(Runnable r) |
인터페이스 객체 |
Thread(Runnable r, String s) |
인터페이스 객체와 스레드 이름 |
<Thread 메소드>
static void sleep(long msec) throws Interrupted Exception |
msec에 지정된 밀리초 동안 대기 |
String getName() |
스레드의 이름을 s로 설정 |
void setName(String s) |
스레드의 이름을 s로 설정 |
void start() |
스레드를 시작 run() 메소드 호출 |
int getPriority() |
스레드의 우선 순위를 반환 |
void setpriority(int p) |
스레드의 우선순위를 p값으로 |
boolean isAlive() |
스레드가 시작되었고 아직 끝나지 않았으면 true 끝났으면 false 반환 |
void join() throws InterruptedException |
스레드가 끝날 때 까지 대기 |
void run() |
스레드가 실행할 부분 기술 (오버라이딩 사용) |
void suspend() |
스레드가 일시정지 resume()에 의해 다시시작 할 수 있다. |
void resume() |
일시 정지된 스레드를 다시 시작. |
void yield() |
다른 스레드에게 실행 상태를 양보하고 자신은 준비 상태로 |
2. Thread 생성
- 2가지 방법
① 직접 상속 받아 스레드 생성
② Runnable 인터페이스를 구현해서 생성
- Thread 클래스 이용
- Thread 클래스로 부터 제공되는 run()메소드 오버라이딩해서 사용
- Ex)
class ThreadA extends Thread
{
public void run()
{
// 수행할 문장들 기술
}
}
- 실제 사용
- ThreadA TA = new ThreadA();
TA.start();
<예제 소스코드1 - ThreadTest.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class ThreadTest extends Thread { public void run() { // 인터럽트 됬을때 예외처리 try { for(int i = 0 ; i < 10 ; i++) { // 스레드 0.5초동안 대기 Thread.sleep(500); System.out.println("Thread : " + i); } }catch(InterruptedException e) { System.out.println(e); } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Thread1 { public static void main(String args[]) { ThreadTest t1 = new ThreadTest(); ThreadTest t2 = new ThreadTest(); // 1. 동시에 똑같은 숫자가 나오고(start) /*t1.start(); t2.start();*/ // 2. 번갈아가면서 나옴(run) t1.run(); t2.run(); } } | cs |
<결과1 - .start()>
<결과2 - .run()>
- 현재의 클래스가 이미 다른 클래스로부터 상속 받고 있다면 Runnable 인터페이스를 이용하여 스레드를 생성할 수 있습니다.
- Runnable 인터페이스는 JDK 라이브러리 인터페이스이고 run()메소드만 정의되어 있다.
<예제 소스코드1 - RunnableTest.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Runnable 인터페이스 상속 public class RunnableTest implements Runnable { // Runnable 인터페이스의 run()오버라이딩 public void run() { try // 인터럽트 예외처리 { for (int i=0 ; i<10 ; i++) { // 대기시간 0.2초 Thread.sleep(200); System.out.println("스레드 :" + i); } }catch(InterruptedException e ) { e.printStackTrace(); } } } | cs |
<예제 소스코드2 - Thread2.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Thread2 { public static void main(String args[]) { // Runnable 인터페이스 객체생성 RunnableTest Obj1 = new RunnableTest(); RunnableTest Obj2 = new RunnableTest(); // Runnable 객체를 매개변수로 하여 스레드 객체 th생성 Thread th1 = new Thread(Obj1); Thread th2 = new Thread(Obj2); th1.start(); th2.start(); } } | cs |
<결과>
- run()메소드가 종료하면 스레드는 종료된다.
- 스레드를 계속 실행시킬려면 run()메소드를 무한루프 속에 실행되어야합니다.
- 한번 종료한 스레드는 다시 시작시킬수 없습니다.
- 스레드 객체를 다시 생성해야 합니다.
- 한 스레드에서 다른 스레드를 강제 종료할 수 있습니다.
- 스레드의 상태 6가지
① NEW : 스레드가 생성되었지만 스레드가 아직 실행할 준비가 되지 않았음
② RUNNABLE : 스레드가 실행되고 있거나 실행준비되어 스케쥴링은 기달리는 상태
③ WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기 기다리고 있는 상태(동기화)
④ TIMED_WAITING : 스레드가 sleep(n) 호출로 인해 n 밀리초동안 잠을 자고 있는 상태
⑤ BLOCK : 스레드가 I/O 작업을 요청하면 자동으로 스레드를 BLOCK 상태로 만든다.
⑥ TERMINATED : 스레드가 종료한 상태
- 스레드 상태는 JVM에 의해 기록 관리된다.
3. Daemon Thread
- 동일한 프로세스 안에서 다른 스레드의 수행을 돕는 스레드로 다른 스레드를 서비스 해주면서 다른 스레드가 모두 종료되면 자신도 종료되는 스레드
- 프로그램이 종료되는 것을 막지 않으며 가비지 컬렉터나 메인 스레드가 데몬 스레드입니다.
- 스레드를 생성하고 setDaemon(true)를 설정하면 됨
- 스레드가 시작하기 전에 설정해야 합니다.
<예제 소스코드 - Thread3.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class Thread3 extends Thread { public void run() { try { System.out.println("Daemon Thread Start!"); sleep(10000); System.out.println("Daemon Thread End!"); }catch(Exception e){ } } public static void main(String args[]) { Thread3 t = new Thread3(); // Thread가 종료되지 않기 때문에 끝나질 않음 t.setDaemon(true); t.start(); System.out.println("Main Method End!"); } } | cs |
<결과>
4. 우선 순위(Priority)
- 2개 이상의 스레드가 동작 중일 때 우선 순위를 부여하여 우선 순위가 높은 스레드에게 실행의 우선권을 부여할 수 있습니다.
- 우선 순위를 지정하기 위한 상수를 제공
- static final int MAX_PRIORITY : 우선순위 10 - 가장 높은 우선 순위
- static final int MIN_PRIORITY : 우선순위 1 - 가장 낮은 우선 순위
- static final int NORM_PRIORITY : 우선순위 5 - 보통의 우선 순위
- 스레드 우선 순위는 변경 가능
- void setPriority(int priority)
- int getPriority()
- main()스레드의 우선 순위 값은 초기값이 5
- JVM의 스케쥴링 규칙
- 철저한 우선 순위 기반
- 가장 높은 우선 순위의 스레드가 우선적으로 스케쥴링
- 동일한 우선 순위의 스레드는 돌아가면서 스케쥴링(라운드 로빈)
<예제 소스코드1 - PriorityTest.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class PriorityTest extends Thread { // 출력생성자 PriorityTest(String str) { super(str); } public void run() { try { // 각각 1초 간격으로 스레드 for(int i = 0 ; i < 10 ; i++) { Thread.sleep(1000); System.out.println(getName() + i + "번째 수행"); } }catch(InterruptedException e) { e.printStackTrace(); } } } |
<예제 소스코드2 - Thread4.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Thread4 { public static void main(String args[]) { PriorityTest t1 = new PriorityTest("우선 순위가 낮은 스레드"); PriorityTest t2 = new PriorityTest("우선 순위가 높은 스레드"); t1.setPriority(Thread.MIN_PRIORITY); // 최소 우선 순위 지정 t2.setPriority(Thread.MAX_PRIORITY); // 최대 우선 순위 지정 t1.start(); t2.start(); } } | cs |
<결과>
5. 스레드 종료
- 스스로 종료
- run()메소드에 예외처리에 return을 넣어 스스로 종료
- 타 스레드에서 강제 종료 : interrupt()메소드 호출하면 InterruptedException이 발생
<예제 소스코드1 - ThreadInterrupt.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class ThreadInterrupt extends Thread { // 출력 생성자 ThreadInterrupt(String str) { super(str); } public void run() { try { for (int i = 0 ; i < 10 ; i++) { Thread.sleep(1000); System.out.println(getName() + i + "번쨰 수행"); } }catch(InterruptedException e) { // 인터럽트 걸릴시 System.out.println("스레드 강제 종료"); return; } } } | cs |
<예제 소스코드2 - Thread5.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Thread5 { public static void main(String args[]) { ThreadInterrupt th = new ThreadInterrupt("스레드"); th.start(); // 3초에 인터럽트 try { Thread.sleep(3000); } catch(InterruptedException e) {} th.interrupt(); } } | cs |
<결과>
6. 멀티 스레드 (Multi Thread)
- 여러 개의 스레드가 동시에 수행되면서 공유할 수 있을때
- 공유되는 부분은 상호 배타적으로 사용되어야 합니다.
-Dead Lock 문제
- 멀티 스레드를 사용할 때 주의할 점중의 하나로 프로그램에서 스레드를 잘못 만들면 프로그램의 수행이 이루어 지지 않고 무한 수행하는 Dead Lock을 만들 수 있습니다.
- 임계 영역(critical section)
- 공유 자원을 사용하는 코드영역을 임계영역이라 합니다.
- 이 부분에서는 공유 자원을 동시에 수정할 수 없도록 상호 배타적으로 실행될 수 있도록 작성되어야 합니다.
- 자바에서 상호배제 문제를 해결하는 방법
- 자바는 한 순간에 하나의 스레드만 실행할 수 있는 synchronized method 제공
- 한 스레드가 synchronized method를 수행 중이면 다른 스레드는 대기합니다.
- 처리방법
- 공유 자원에 접근하는 메소드의 앞에 synchronized 메소드로 지정 합니다.
- 공유 자원을 사용하는 영역을 synchronized(객체명)의 블록으로 지정합니다.
<멀티 스레드 X 예제 소스코드1 - Account.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Account { int balance = 1000; public void withdraw(int money) { // 잔고가 찾으려는 금액보다 클 때 if(balance >= money) { try { // 1초 Thread.sleep(1000); }catch(Exception e) { } balance -= money; } } } | cs |
<멀티 스레드 X 예제 소스코드2 - Bank.class>
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 | class Bank extends Thread { static Account obj = new Account(); public Bank(){} public Bank(String name) { super(name); } public void run() { // 무한 반복 while(true) { // 찾는 금액은 랜덤 int money = (int)(Math.random() * 3 + 1) * 100; // 원금이 찾는 금액보다 클 때 if(obj.balance >= money) { System.out.println(getName() + " : 원본의 balance:" + obj.balance); System.out.println(getName() + " : 찾는 금액:" + money); obj.withdraw(money); System.out.println(getName() + " : 수정된 balance:" + obj.balance); } else { System.out.println("잔액 부족"); break; } } } } | cs |
<멀티 스레드 X 예제 소스코드3 - Thread6.class>
1 2 3 4 5 6 7 8 9 10 11 | public class Thread6 { public static void main(String args[]) { Bank t1 = new Bank("ATM"); Bank t2 = new Bank("은행"); t1.start(); t2.start(); } } | cs |
<결과 - 돈을 찾았음에도불고하고 갱신이 바로바로 되지 않는다.>
<멀티 스레드 O 예제 소스코드1 - Account.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Account { int balance = 1000; public void withdraw(int money) { if(balance >= money) { try { Thread.sleep(1000); }catch(Exception e) { } balance -= money; } } } | cs |
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 | public class Bank extends Thread { static Account obj = new Account(); public Bank() { } public Bank(String name) { super(name); } public void run() { while(true) { // 멀티 스레드 synchronized(obj) { int money = (int)(Math.random() * 3 + 1) *100; if(obj.balance >= money) { System.out.println(getName() + " : 원본의 balance : " + obj.balance); System.out.println(getName() + ": 찾는 금액 : " + money); obj.withdraw(money); System.out.println(getName() + " : 수정된 balance : " + obj.balance); }else { System.out.println("잔액 부족"); break; } } } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 | public class Thread6 { public static void main(String args[]) { Bank t1 = new Bank("ATM"); Bank t2 = new Bank("은행"); t1.start(); t2.start(); } } | cs |
<결과 - 돈을 찾게 되면 바로바로 갱신이 된다.>
7. 타이머 활용
- public class Timer extends Object
- 백그라운드 Thread로 실행되는 일을 1회 또는 정기적으로 반복 실행되도록 스케쥴링 해주는 클래스
<생성자>
Timer() |
새로운 타이머를 작성 |
Timer(boolean isDaemon) |
daemon으로 실행되는 타이머 |
Timer(String name) |
지정된 이름의 thread를 가지는 타이머 |
Timer(String name, boolean isDaemon) |
지정된 이름의 thread를 가지는 새로운 타이머 |
<메소드>
void cancel() |
현재 스케쥴 되고 있는 일을 파기해서 타이머를 종료 |
void schedule(TimerTask task, Date time) |
지정한 시간으로 지정한 일이 실행되도록 스케쥴 |
void schedule(TimerTask task, Date firstTime, long period) |
지정한 일이 지정한 시간에 개시되어 기간을 가지고 반복 |
void schedule(TimerTask task, long delay) |
지정한 지연 후에 지정한 일이 실행되도록 스케쥴 |
void schedule(TimerTask task, long delay, long period) |
지정한 일이 지정한 지연 후에 개시되어 기간을 가지고 반복 |
- public abstract class TimerTask extends Object implements Runnable
- Timer에 해 1회 또는 반복해 실행하도록 스케쥴 되는 일
<생성자>
protected TimerTask() |
새로운 타이머 일이 작성 |
<메소드>
boolean cancel() |
이 타이머 일을 취소 합니다. |
abstract void run() |
이 타이머 일에 실행되는 액션 입니다. |
long scheduledExecutionTime() |
이 일이 최근 실제로 실행하도록 스케쥴 되었다라는 실행 시간을 돌려줍니다. |
<타이머 예제 소스코드1 - Mytask.class>
1 2 3 4 5 6 7 8 9 10 | import java.util.TimerTask; public class MyTask extends TimerTask { // "타이머 호출" public void run() { System.out.println("타이머 호출"); } } | cs |
<타이머 예제 소스코드2 - TimerTest.class>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import java.util.Timer; public class TimerTest { public static void main(String args[]) { MyTask task =new MyTask(); // 타이머 객체 생성 Timer timer = new Timer(); // 1초후에 0.5초 간격으로 반복 timer.schedule(task, 1000, 500); try { // 5초후에 스레드를 멈춘다. Thread.sleep(5000); }catch(InterruptedException exc) { timer.cancel(); } // 타이머 취소 timer.cancel(); } } | cs |
<결과>
'조재연의 Java 개꿀떡 > 조재연의 Java 기초 개꿀떡' 카테고리의 다른 글
자바(Java)의 기초 박살내기 - Java GUI② (0) | 2017.07.18 |
---|---|
자바(Java)의 기초 박살내기 - Java GUI① (0) | 2017.07.14 |
자바(Java)의 기초 박살내기 - 예외처리(Exception) & java.lang 패키지 (0) | 2017.07.06 |
자바(Java)의 기초 박살내기 - 배열(array) (0) | 2017.07.05 |
자바(Java)의 기초 박살내기 - 상속(Inheritance)과 다형성(Polymorphism) (1) | 2017.07.04 |