Thread Status

 

상태에 대해서 설명해드리자면

New 스레드의 객체가 생성된 상태를 말합니다

Thread 클래스를 상속받거나 Runnable 인터페이스를 구현한 클래스를 new(생성한) 상태입니다

Runnable 생성된 스레드에 start 메소드를 호출했을때의 상태입니다

스레드가 실제로 실행되지는 않았지만 준비단계에 있는 것을 말합니다

Running 스레드가 스케쥴러에 의해서 실제로 동작하고있는 상태입니다

run 메소드가 실행되고 있겠죠?

Blocking Running 상태이던 스레드가 run 메소드를 모두 마치지 않고 중간에 중지된 상태입니다

입출력이나 인터럽트의 요청이 있을 경우 발생합니다 sleep, yield, join 메소드 호출시에도 발생하구요

Dead Running 상태이던 스레드가 run 메소드의 작업을 모두 처리하고 제거되는 상태입니다

 

생성과 실행은 이미 살펴봤으니 Blocking 관련된 메소드들에 대해서 알아봐야겠군요

언급했던 세가지 메소드 sleep, yield, join 메소드를 공부해봅시다

 

세가지 메소드를 간단히 요약하면 이렇습니다

  • sleep 메소드는 지정한 시간만큼 스레드를 Blocking 합니다
  • yield 메소드는 우선순위가 동일한 스레드에게 양보하도록 유도하는 메소드입니다
  • join 메소드는 메소드를 호출한 스레드가 종료될때까지 스레드를 생성한 스레드를 Blocking 합니다


그럼 실제로 sleep 메소드를 사용하는 소스코드를 작성해보겠습니다

 

package thread;

 

public class SleepTest implements Runnable {

       private int dan;

 

       /**

        * @param args

        */

       public SleepTest(int dan) {

              this.dan = dan;

       }

 

       @Override

       public void run() {

              long sleepTime = (long) (Math.random() * 500);

              System.out.println(dan + "단은 " + sleepTime + "만큼 sleep 실행");

              try {

                     Thread.sleep(sleepTime);

              } catch (InterruptedException e) {

                     e.getMessage();

              }

              for (int i = 1; i <= 9; i++) {

                     System.out.println(dan + ":" + dan + " * " + i + " = " + dan * i);

              }

 

       }

 

} 

 

 

 

package thread;

 

public class MainClass {

 

       /**

        * @param args

        */

       public static void main(String[] args) {

              Thread thread2 = new Thread(new SleepTest(2));

              Thread thread3 = new Thread(new SleepTest(3));

              Thread thread4 = new Thread(new SleepTest(4));

              Thread thread5 = new Thread(new SleepTest(5));

              Thread thread6 = new Thread(new SleepTest(6));

              thread2.start();

              thread3.start();

              thread4.start();

              thread5.start();

              thread6.start();

       }

 

}

 

2단부터 6단까지의 구구단을 각각의 스레드로 출력하는 내용이네요

하지만 스레드마다 랜덤한 sleep 시간을 두어 순차적으로 실행되는 모양입니다 결과를 보면..

3단은 92만큼 sleep 실행

6단은 74만큼 sleep 실행

4단은 43만큼 sleep 실행

5단은 298만큼 sleep 실행

2단은 51만큼 sleep 실행

4:4 * 1 = 4

4:4 * 2 = 8

4:4 * 3 = 12

4:4 * 4 = 16

4:4 * 5 = 20

4:4 * 6 = 24

4:4 * 7 = 28

4:4 * 8 = 32

4:4 * 9 = 36

2:2 * 1 = 2

2:2 * 2 = 4

2:2 * 3 = 6

2:2 * 4 = 8

2:2 * 5 = 10

2:2 * 6 = 12

2:2 * 7 = 14

2:2 * 8 = 16

2:2 * 9 = 18

6:6 * 1 = 6

6:6 * 2 = 12

6:6 * 3 = 18

6:6 * 4 = 24

6:6 * 5 = 30

6:6 * 6 = 36

6:6 * 7 = 42

6:6 * 8 = 48

6:6 * 9 = 54

3:3 * 1 = 3

3:3 * 2 = 6

3:3 * 3 = 9

3:3 * 4 = 12

3:3 * 5 = 15

3:3 * 6 = 18

3:3 * 7 = 21

3:3 * 8 = 24

3:3 * 9 = 27

5:5 * 1 = 5

5:5 * 2 = 10

5:5 * 3 = 15

5:5 * 4 = 20

5:5 * 5 = 25

5:5 * 6 = 30

5:5 * 7 = 35

5:5 * 8 = 40

5:5 * 9 = 45

정말 sleep 시간이 짧았던것부터 긴것의 순서대로 스레드가 실행되었음을 있습니다

sleep 메소드의 파라미터에는 밀리초(천분의 1) 단위의 long타입 리터럴 혹은 변수를 지정해주시면 됩니다

 

 

다음은 yield 메소드입니다 같은 우선순위에게 양보한다니 대체 무슨 뜻일까요? 소스코드를 보겠습니다

 

package thread;

 

public class YieldTest implements Runnable {

       private int dan;

 

       public YieldTest(int dan) {

              this.dan = dan;

       }

 

       @Override

       public void run() {

              if (dan == 6) {

                     System.out.println("6단은 yield(양보)합니다");

                     Thread.yield();

              }

              for (int i = 1; i <= 9; i++) {

                     System.out.println(dan + ":" + dan + " * " + i + " = " + dan * i);

              }

 

       }

}

 

  

package thread;

 

public class MainClass2 {

 

       /**

        * @param args

        */

       public static void main(String[] args) {

              Thread thread2 = new Thread(new YieldTest(2));

        Thread thread3 = new Thread(new YieldTest(3));

        Thread thread4 = new Thread(new YieldTest(4));

        Thread thread5 = new Thread(new YieldTest(5));

        Thread thread6 = new Thread(new YieldTest(6));

        thread2.setPriority(Thread.MAX_PRIORITY);

        thread3.setPriority(Thread.NORM_PRIORITY);

        thread4.setPriority(Thread.MIN_PRIORITY);

        thread5.setPriority(Thread.MIN_PRIORITY);

        thread6.setPriority(Thread.MIN_PRIORITY);

        thread2.start();

        thread3.start();

        thread4.start();

        thread5.start();

        thread6.start();

 

       }

 

}

 

setPriority 메소드는 스레드간의 우선순위를 설정하는 일을 합니다 1~10까지의 값을 넣을 있구요

소스코드에서처럼 Thread 클래스의 static 상수를 사용해도 됩니다

MAX_PRIORITY 10, NORM_PRIORITY 5, MIN_PRIORITY 1 같습니다

숫자가 높을수록 우선순위가 높다는 얘기입니다 이제 실행결과를 보겠습니다

2:2 * 1 = 2

2:2 * 2 = 4

2:2 * 3 = 6

2:2 * 4 = 8

2:2 * 5 = 10

2:2 * 6 = 12

2:2 * 7 = 14

2:2 * 8 = 16

2:2 * 9 = 18

4:4 * 1 = 4

4:4 * 2 = 8

3:3 * 1 = 3

4:4 * 3 = 12

6단은 yield(양보)합니다

5:5 * 1 = 5

5:5 * 2 = 10

5:5 * 3 = 15

5:5 * 4 = 20

5:5 * 5 = 25

5:5 * 6 = 30

5:5 * 7 = 35

5:5 * 8 = 40

5:5 * 9 = 45

6:6 * 1 = 6

4:4 * 4 = 16

3:3 * 2 = 6

3:3 * 3 = 9

3:3 * 4 = 12

3:3 * 5 = 15

3:3 * 6 = 18

3:3 * 7 = 21

3:3 * 8 = 24

3:3 * 9 = 27

4:4 * 5 = 20

4:4 * 6 = 24

4:4 * 7 = 28

4:4 * 8 = 32

4:4 * 9 = 36

6:6 * 2 = 12

6:6 * 3 = 18

6:6 * 4 = 24

6:6 * 5 = 30

6:6 * 6 = 36

6:6 * 7 = 42

6:6 * 8 = 48

6:6 * 9 = 54

6단이 시작되려던 4:4 * 3 = 12 다음부분에서 6단의 스레드는 5단에게 양보를 하고 나중에 시작됐네요. 하지만 결과가 항상 나오는 것은 아닙니다 결국에는 스레드 스케쥴러에 의해서 결정되는 일이기 때문입니다

 

현재 실행중이던 스레드를 Blocking 하고 join 요청한 스레드가 작업을 마치길 기다리는지 확인해보겠습니다.

 

package thread;

 

public class JoinTest implements Runnable {

 

       private int start;

       private int end;

       private int result;

 

       public JoinTest(int start, int end) {

              this.start = start;

              this.end = end;

       }

 

       @Override

       public void run() {

              System.out.println(start + "부터 " + end + "까지 더해봅니다");

              for (int i = start; i <= end; i++) {

                     result += i;

              }

       }

 

       public int getResult() {

              return result;

       }

 

}

 

 

package thread;

 

public class MainClass3 {

 

       /**

        * @param args

        */

       public static void main(String[] args) {

              JoinTest joinTest = new JoinTest(1, 100);

              Thread thread = new Thread(joinTest);

              thread.start();

              try {

                     // thread.join();

              } catch (Exception e) {

                     e.getMessage();

              }

              System.out.println(joinTest.getResult());

              System.out.println("main() 종료");

 

       }

 

}

 

 

여기서는 join 메소드를 주석처리 해봤습니다 프로그램의 의도는 JoinTest 넘기는 파라미터(시작값,최대값) 가지고

최소값부터 최대값까지 합산한 결과를 main 메소드의 마지막에 출력해야합니다

thread 모든 작업을 마칠때까지 main 스레드는 대기해줘야합니다 그런데 결과를 보시면..

0

1부터 100까지 더해봅니다

main() 종료

이런~ 아직 thread 1부터 100까지 더하는 연산을 마치지도 못했는데 main 멋대로 결과값을 출력하고 종료되어버렸습니다

 

다시 join 주석을 풀면 결과는 이렇습니다

1부터 100까지 더해봅니다

5050

main() 종료

이제 의도한대로 결과가 나왔습니다 1부터 100까지 더하는 동안 main 메소드의 다음 명령문들은 실행되지 않고 대기했네요

연산이 끝나자마자(thread 종료되자마자) 부리나케 총합을 출력하고는 종료하는군요

 

출처 : http://n6lab.tistory.com/11

 

 

 


Thread Deadlock Example




Deadlock describes a situation where two or more threads are blocked forever, waiting for each other. Deadlock occurs when multiple threads need the same locks but obtain them in different order. A Java multithreaded program may suffer from the deadlock condition because the synchronized keyword causes the executing thread to block while waiting for the lock, or monitor, associated with the specified object. Here is an example:

Example:

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
   
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

When you compile and execute above program, you find a deadlock situation and below is the output produced by the program:

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

Above program will hang forever because neither of the threads in position to proceed and waiting for each other to release the lock, so you can come out of the program by pressing CTRL-C.

Deadlock Solution Example:

Let's change the order of the lock and run the same program to see if still both the threads waits for each other:

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
   
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");
            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

So just changing the order of the locks prevent the program in going deadlock situation and completes with the following result:

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

Above example has been shown just for making you the concept clear, but its a more complex concept and you should deep dive into it before you develop your applications to deal with deadlock situations.

'Java > Threads' 카테고리의 다른 글

Thread Synchronization  (0) 2013.02.07
Thread & Runnable  (0) 2013.02.07
Posted by Steven J.S Min
,