일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 업캐스팅
- 다운캐스팅
- while
- Random
- 전자정부
- 추상클래스
- jquery
- Login with OAuth Authentication
- 스프링
- 형변환
- 전체텍스트
- 가변인자
- 다형성
- Full text
- 상속예제
- 전체
- 자바 야구게임
- 25가지 효율적인 sql작성법
- 페이징
- 단축키
- 이클립스
- Validations
- full text indexing
- 상속
- IBatis procedure
- 자바
- 로또
- 야구게임
- angular2
- Today
- Total
nalaolla
Java 쓰레드(Thread) 본문
Java 쓰레드(Thread)
Thread API : http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
Thread Group API : http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadGroup.html
1. 프로세스와 쓰레드
- 프로세스(Process) : 실행 중인 프로그램(Program), 자원(resources)과 쓰레드로 구성, 공장에 비유된다.
- 쓰레드(Thread) : 프로세스 내에서 실제 작업을 수행한다, 모든 프로세스는 하나 이상의 쓰레드를 가지고 있다, 일꾼에 비유된다.
- 싱글쓰레드 프로세스 = 자원 + thread
- 멀티쓰레드 프로세스 = 자원 + thread + thread + . . .
2. 멀티쓰레딩
- 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것
- 아주 짧은 시간 동안 여러 작업을 번갈아 가며 수행함으로써 동시에 여러 작업이 수행되는 것처럼 보이게 하는 것
- CPU의 사용률을 향상시킨다.
- 자원을 보다 효율적으로 사용할 수 있다.
- 사용자에 대한 응답성이 향상된다.
- 작업이 분리되어 코드가 간결해진다.
- 동기화(synchronization), 교착상태(deadlock) 같은 문제들을 고려해야 한다.
- 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야 한다.
3. 쓰레드의 구현과 실행
- 구현하는 두가지 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다.
- 다른 클래스를 상속받을때에는 Runnable 인터페이스를 구현하는 방법이 일반적이다.
- 한번 사용한 쓰레드는 다시 재사용할 수 없다.
- class Thread1 extends Thread {
- public void run() {
- for (int i = 0; i < 5; i++) {
- System.out.println(getName()); // 조상 Thread의 getName()을 호출
- } // for
- } // run
- } // Thread1
- class Thread1_1 implements Runnable {
- @Override
- public void run() {
- for (int i = 0; i < 5; i++) {
- // Thread.currentThread(); // 현재 실행 중인 Thread를 반환한다.
- System.out.println(Thread.currentThread().getName());
- } // for
- } // run
- } // Thread1_1
- public class ThreadTest {
- public static void main(String[] args) {
- Thread1 t1 = new Thread1();
- Thread1_1 t = new Thread1_1();
- Thread t2 = new Thread(t); // 생성자 Thread(Runnable target)
- t1.start();
- t2.start();
- } // main
- } // ThreadTest
- /*
- * 결과
- *
- * Thread-0
- * Thread-0
- * Thread-0
- * Thread-0
- * Thread-0
- * Thread-1
- * Thread-1
- * Thread-1
- * Thread-1
- * Thread-1
- */
4. start(), run()
- run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 속한 메서드 하나를 호출하는 것이다.
- start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서 저장되게 한다.
- 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
- class Human2 extends Thread {
- String name;
- public Human2(String name) {
- this.name = name;
- } // Human2 생성자
- public void run() {
- for (int i = 1; i <= 10; i++) {
- System.out.println(name + " 이(가) " + "음식" + " 을(를) 먹어요 " + i);
- try {
- Thread.sleep(500);
- } catch (Exception e) {
- e.printStackTrace();
- } // try - catch
- } // for
- } // run
- } // Human
- public class ThreadTest2 {
- public static void main(String[] args) {
- Human2 human1 = new Human2("갱짱");
- Human2 human2 = new Human2("이갱짱");
- //human1.run();
- //human2.run();
- human1.start();
- human2.start();
- } // main
- } // ThreadTest
5. 싱글쓰레드와 멀티쓰레드
- 싱글쓰레드 : 두 개의 작업을 하나의 쓰레드로 처리하는 경우
- 멀티쓰레드 : 두 개의 작업을 두개의 쓰레드로 처리하는 경우
- 단순히 CPU만을 사용하는 계산작업이라면 작업전환(context switching)에 시간이 안걸리는 싱글쓰레드가 효율적이다.
- 하지만 CPU이외의 자원을 사용하는 작업의 경우에는 멀티쓰레드 프로세스가 더 효율적이다.
6. 쓰레드의 우선순위(Proiority)
- 우선순위라는 속성(멤버변수)을 가지고 있는데, 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다.
- 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 할 수 있다.
- 쓰레드가 가질 수 있는 우선순위의 범위는 1 ~ 10 이며, 숫자가 높을수록 우선순위가 높다.
- 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다.
- // void setPriority(int newPriority) : 쓰레드의 우선 순위를 지정한 값으로 변경한다.
- // int getPriority() : 쓰레드의 우선순위를 반환한다.
- // public static final int MAX_PRIORITY = 10 : 최대우선순위
- // public static final int MAX_PRIORITY = 1 : 최소우선순위
- // public static final int MAX_PRIORITY = 5 : 보통우선순위
- class Thread_1 extends Thread {
- public void run() {
- for (int i = 0; i < 300; i++) {
- System.out.print("-");
- for (int x = 0; x < 10000000; x++); // 작업을 지연시키기 위한 for 문
- } // for
- } // run
- } // Thread_1
- class Thread_2 extends Thread {
- public void run() {
- for (int i = 0; i < 300; i++) {
- System.err.print("|");
- for (int x = 0; x < 10000000; x++); // 작업을 지연시키기 위한 for 문
- } // for
- } // run
- } // Thread_2
- public class ThreadPriorityTest {
- public static void main(String[] args) {
- Thread_1 th1 = new Thread_1();
- Thread_2 th2 = new Thread_2();
- th2.setPriority(8);
- System.out.println("Priority of th1 (-) : " + th1.getPriority());
- System.out.println("Priority of th2 (|) : " + th2.getPriority());
- th1.start();
- th2.start();
- } // main
- } // ThreadPriorityTest
- /*
- * 결과
- *
- * Priority of th1 (-) : 5
- * Priority of th2 (|) : 7
- * -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
6. 쓰레드 그룹(thread group)
- 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것.
- 쓰레드 그룹에 다른 쓰레드 그룹을 포함 시킬 수 있다. 보안상의 이유로 도입된 개념이다.
- 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다.
- 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 한다.
- 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 'main쓰레드 그룹'에 속한다.
- 쓰레드를 쓰레드 그룹에 포함시키려면 Thread의 생성자를 이용해야한다.
7. 데몬 쓰레드(daemon thread)
- 일반 쓰레드(non-daemon thread)의 작업을 돕는 보조적인 역할을 수행한다.
- 일반 쓰레드가 모두 종료되면 자동적으로 종료된다.
- 가비지컬렉터, 워드프로세서의 자동저장, 화면자동갱신 등에 사용된다.
- 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
- boolean is Daemon() : 쓰레드가 데몬 쓰레드인지 확인한다. 데몬 쓰레드이면 true를 반환한다.
- void setDaemon(boolean on) : 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다. 매개변수 on의 값을 true로 지정하면 데몬 쓰레드가 된다.
- setDaemon은 반드시 start()를 호출하기 전에 실행되어야 한다.
8. 쓰레드의 실행제어
- 쓰레드의 생명 주기
- 쓰레드의 스케줄링과 관련된 메서드
반환형 | 메서드 | 설명 |
void | interrupt() | sleep()이나 join()에 의해 일시정지상태인 쓰레드를 실행 대기 상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지상태를 벗어나게 된다. |
void void | join() join(long millis) join(long millis, int nanos) | 지정된 시간동안 쓰레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. |
void | resume() | suspend()에 의해 일시정지상태에 있는 쓰레드를 실행대기상태로 만든다. |
static void | sleep(long millis) sleep(long millis, int nanos) | 지정된 시간(천분의 일초 단위)동안 쓰레드를 일시정지시킨다. 지정한 시간이 지나고 나면, 자동적으로 다시 실행대기상태가 된다. |
void | stop() | 쓰레드를 즉시 종료시킨다. 교착상태(dead-lock)에 빠지기 쉽기 때문에 deprecated 되었다. |
void | suspend() | 쓰레드를 일시 정지시킨다. resume()을 호출하면 |
static void | void yield() | 실행 중에 다른 쓰레드에게 양보(yield)하고 실행대기상태가 된다. |
- 쓰레드의 상태
상태 | 설명 |
NEW | 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태 |
RUNNABLE | 실행 중 또는 실행 가능한 상태 |
BLOCKED | 동기화블럭에 의해서 일시정지된 상태(LOCK이 풀릴 때 까지 기다리는 상태) |
WAITING, TIMED_WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시 정지상태, TIMED_WAITING은 일시정지시간이 지정된 경우를 의미 |
TERMINATED | 쓰레드의 작업이 종료된 상태 |
- 쓰레드를 생성하고 start()를 호출하면 바로 실행되는 것이 아니라 실행대기열(큐와같은 구조)에 저장되어 자신의 차례가 될 때까지 기다려야 한다.
- 실행대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.
- 주어진 실행시간이 다되거나 yield()를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.
- 실행 중에 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될 수 있다. I/O block은 입출력작업에서 발생하는 지연상태를 말한다.
- 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), interrupt()가 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.
- 실행을 모두 마치거나 stop()이 호출되면 쓰레드는 소멸된다.
- // 한 쓰레드의 작업의 중간에 다른 쓰레드의 작업이 필요할 때 join()을 사용한다.
- class ThreadJoin_1 extends Thread {
- public void run() {
- for (int i = 0; i < 300; i++) {
- System.out.print("-");
- } // for
- } // run
- } // ThreadJoin_1
- class ThreadJoin_2 extends Thread {
- public void run() {
- for (int i = 0; i < 300; i++) {
- System.out.print("+");
- } // for
- } // run
- } // ThreadJoin_1
- public class ThreadJoinTest {
- public static void main(String[] args) {
- ThreadJoin_1 th1 = new ThreadJoin_1();
- ThreadJoin_2 th2 = new ThreadJoin_2();
- th1.start();
- try {
- th1.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- } // try - catch
- System.out.println();
- th2.start();
- } // main
- } // ThreadJoinTest
- /*
- * 결과
- *
- * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
- // sleep()는 쓰레드를 지정된 시간동안 일시정지 상태가 되도록 한다. 천분의 일초 단위이다.
- // suspend()가 호출되면 쓰레드는 일시정지 상태가 되고
- // resume()이 호출되면 다시 실행 대기 상태가 된다.
- // stop()을 호출하면 쓰레드는 즉시 종료된다.
- // interrupt()는 InterruptedException을 발생시켜서, sleep(), join(), wait()에 의해 일시정지상태인 쓰레드를 실행 대기 상태로 만든다.
- // 그러나, 호출되었을 때 sleep(), join(), wait()에 의한 일시정지상태가 아니라면 아무 일도 일어나지 않는다.
- class MyTherdTest implements Runnable {
- boolean suspended = false;
- boolean stopped = false;
- Thread th;
- MyTherdTest(String name) {
- th = new Thread(this, name); // Thread(Runnable r, String name)
- } // MyMyTherdTest 생성자
- @Override
- public void run() {
- String name = Thread.currentThread().getName();
- while (!stopped) {
- if (!suspended) {
- System.out.println(name);
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- System.out.println(name + " - interrupted");
- } // try - catch
- } else {
- Thread.yield();
- } // if
- } // while
- System.out.println(name + " - stopped");
- } // run
- public void suspend() {
- suspended = true;
- th.interrupt();
- System.out.println("interrupt() in suspend()");
- } // suspend
- public void resume() {
- suspended = false;
- } // resume
- public void stop() {
- stopped = true;
- th.interrupt();
- System.out.println("interrupt() in stop()");
- } // stop
- public void start() {
- th.start();
- } // start
- } // MyTheradTest
- public class TheradTest {
- public static void main(String[] args) {
- MyTherdTest th1 = new MyTherdTest("●");
- MyTherdTest th2 = new MyTherdTest("★★");
- MyTherdTest th3 = new MyTherdTest("◆◆◆");
- th1.start();
- th2.start();
- th3.start();
- try {
- Thread.sleep(2000);
- th1.suspend();
- Thread.sleep(2000);
- th2.suspend();
- Thread.sleep(3000);
- th1.resume();
- Thread.sleep(3000);
- th1.stop();
- th2.stop();
- Thread.sleep(2000);
- th3.stop();
- } catch (Exception e) {
- e.printStackTrace();
- } // try - catch
- } // main
- } // TheradTest
- /*
- * 실행 결과
- ●
- ◆◆◆
- ★★
- ●
- ◆◆◆
- ★★
- ●
- interrupt() in suspend()
- ◆◆◆
- ★★
- ● - interrupted
- ★★
- ◆◆◆
- ◆◆◆
- interrupt() in suspend()
- ★★
- ★★ - interrupted
- ◆◆◆
- ◆◆◆
- ◆◆◆
- ●
- ◆◆◆
- ●
- ◆◆◆
- ●
- ◆◆◆
- interrupt() in stop()
- interrupt() in stop()
- ★★ - stopped
- ● - stopped
- ◆◆◆
- ◆◆◆
- interrupt() in stop()
- ◆◆◆ - interrupted
- ◆◆◆ - stopped
- */
9. 쓰레드의 동기화
- 멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업을 하기 때문에 동기화가 필요하다.
- 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서 데이터의 일관성을 유지한다.
- synchronized 키워드를 통해 작업과 관련된 공유데이터에 lock를 건다.
- 특정 객체에 lock을 걸 때 : synchronized(객체의 참조변수) { /* ... */ }
- 메서드에 lock을 걸 때 : public synchronized void calcSum() { /* ... */ }
- class ATM implements Runnable {
- private long depositeMoney = 10000;
- @Override
- public void run() {
- for (int i = 0; i < 10; i++) {
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- e.printStackTrace();
- } // try - catch
- if (getDepositeMoney() <= 0)
- break;
- synchronized (this) {
- withDraw(1000);
- } // synchronized
- } // for
- } // run
- public void withDraw(long howMuch) {
- if (getDepositeMoney() > 0) {
- depositeMoney -= howMuch;
- System.out.print(Thread.currentThread().getName() + " , ");
- System.out.printf("잔액 : %,d 원 %n", getDepositeMoney());
- } else {
- System.out.print(Thread.currentThread().getName() + " , ");
- System.out.println("잔액이 부족합니다");
- } // if
- } // withDraw
- public long getDepositeMoney() {
- return depositeMoney;
- } // getDepositeMoney
- } // ATM
- public class SynchronizedTest {
- public static void main(String[] args) {
- ATM atm = new ATM();
- Thread mother = new Thread(atm, "mother");
- Thread son = new Thread(atm, "son");
- mother.start();
- son.start();
- } // main
- } // SynchronizedTest
10. wait(), notify()
- 동기화의 효율을 높이기 위해 사용한다.
- Object 클래스의 정의되어 있으므로, 모든 객체에서 호출이 가능하다.
- 동기화 블록내에서만 사용할 수 있다.
- wait() : 객체의 lock을 풀고 해당 객체의 쓰레드를 대기상태로 둔다.
- notify() : 대기중인 쓰레드 중의 하나를 깨운다.
- notifyAll() : 대기중인 모든 쓰레드를 깨운다. 호출된 객체의 대기 중인 쓰레드만 해당 된다.
'JAVA > 27. Thread' 카테고리의 다른 글
자바 쓰레드 기초(Java Thread Basic) (0) | 2016.02.16 |
---|