개발/Java

자바 스레드 덤프 Thread Dump 분석

728x90

Synchronized Block 키워드는 다음 네가지 유형의 블록에 쓰인다.

 

  • 인스턴스 메소드에 Synchorinzed 키워드: 인스턴스를 기준으로 이뤄짐. 
  • 스태틱메소드에 Synchronized 키워드  : 클래스를 기준으로 이뤄짐. JVM안애 클래스 객체는 클래스당 하나만 존재 가능하므로, 오직 한 스레드만 동기화된 스태틱 메소드를 실행할 수 있다.
  • 인스턴스 메소드 코드블록 : 특정 블록에 대해서만 동기화. 동기화 블록 안에 전달된 모니터 객체를 기준으로 동기화 됨.
  • 스태틱 메소드 코드블록 : synchronzied 블록의 매개변수에 클래스를 넘겨, 해당 클래스를 기준으로 동기화 된다.

위를 기반으로 아래의 4가지 시나리오 대로 스레드 덤프를 분석해보고자 한다.

 

  • 일반메서드에서 synchronized 블록을 통해 다른 스레드가 Blocked된 상황
  • 일반메서드를 synchronzied로 선언하여 다른 스레드가 Blocked된 상황
  • 스태틱 메서드안에서 synchronized 구문을 통해 다른 스레드가 Blocked된 상황
  • 스태틱메서드를 synchronized로 선언하여 다른 스레드가 Blocked된 상황

스레드 덤프를 분석하기 위해 아래의 두가지 과정이 필요하다. (Mac OS 기준)

  • JVM 프로세스의 id 구하기
  • 해당 프로세스의 스레드 덤프 파일로 저장하기

1. JVM 프로세스의 id 구하기 (프로세스 id만 출력)

ps -ef | grep java | grep -v grep | awk '{print $2}'  

+ jps 명령어를 통해 더 편리하게 프로세스 id를 구할 수 있다.

jps [ options ] [ hostid ]
jps -v

2.해당 프로세스의 스레드 덤프 파일로 저장하기

 

 jstack -l {pid}  > {저장할 directory}

예)

jstack -l 38227  > /Users/gimminseong/thread-dump2.txt

 

 

 

  • 일반메서드를 synchronized로  선언하여 다른 스레드가 Blocked된 상황

아래와 같이 시나리오를 코드로 구현해보았다.

 

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                testSync.test1();
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                testSync.test1();
            }

        });
        t1.start();
        t2.start();



class TestSync{
    public synchronized void test1() {
        System.out.println("test1");
            try {
                System.out.println("test1 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }

    }
}

힙덤프를 확인해보면

Thread-0에서 Object객체의 모니터락을 소유하고 있고

Thread-1에서 Object객체의 모니터락을 기다리고 있는 것을 확인할 수 있다.

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fd9ce85a000 nid=0xa603 waiting on condition [0x0000700002aff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.TestSync.test3(Main.java:147)
	- locked <0x000000071569ed00> (a java.lang.Object)
	at com.company.Main$1.run(Main.java:39)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fd97e011000 nid=0x5d03 waiting for monitor entry [0x0000700002c02000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.company.TestSync.test4(Main.java:157)
	- waiting to lock <0x000000071569ed00> (a java.lang.Object)
	at com.company.Main$2.run(Main.java:54)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

 

 

 

  • 일반메서드 내부에서 synchronzied 블록을 통해 다른 스레드가 Blocked된 상황

아래와 같이 시나리오를 코드로 구현해보았다.

 

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
	            testSync.test3(object);
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                testSync.test3(object);
            }

        });
        t1.start();
        t2.start();



class TestSync{
    public  void test3() {
        Object object = new Object();
        System.out.println("test3");
        synchronized (object){
            try {
                System.out.println("test3 sleep start");
                Thread.sleep(1000 * 60 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

+ 보통 모니터락 객체는 synchronized block이 위치하는 클래스에서 정의하여 컨트롤 한다.

 

그 이유는 ? : 

 

 

힙덤프를 확인해보면

Thread-0에서 해당 메서드가 포함된 객체모니터락을 소유하고 있고

Thread-0에서 Lock관리를 위해 생성한 Object의 모니터락을 소유하고 있고

Thread-1에서 해당 메서드가 포함된 객체의 모니터락을 기다리고 있는 것을 확인할 수 있다.

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fc300020800 nid=0xa303 waiting on condition [0x000070000a6a3000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.TestSync.test1(Main.java:126)
	- locked <0x00000007156a24c0> (a com.company.TestSync)
	at com.company.Main$1.run(Main.java:40)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None
    
"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fc31982e000 nid=0x5903 waiting for monitor entry [0x000070000a7a6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.company.TestSync.test2(Main.java:133)
	- waiting to lock <0x00000007156a24c0> (a com.company.TestSync)
	at com.company.Main$2.run(Main.java:56)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

 

 

  • 스태틱 메서드안에서 synchronized 구문을 통해 다른 스레드가 Blocked된 상황

아래와 같이 시나리오를 코드로 구현해보았다.

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t1 sleep start");
                    test3();
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 sleep start");
                test4();
            }
        });
        t1.start();
        t2.start();
        
        
    public static void test3() {
        System.out.println("test3");
        synchronized (Main.class){
            try {
                System.out.println("test3 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public static void test4() {
        System.out.println("test4");
        synchronized (Main.class){
            try {
                System.out.println("test4 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

 

힙덤프를 확인해보면

Thread-0에서 Main 클래스에 대해 모니터락을 소유하고 있고

Thread-1에서 Main 클래스의 모니터락을 기다리고 있는 것을 확인할 수 있다.

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007feae4847800 nid=0xa303 waiting on condition [0x000070001124b000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.Main.test3(Main.java:72)
	- locked <0x000000071569c3b0> (a java.lang.Class for com.company.Main)
	at com.company.Main$1.run(Main.java:41)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"Thread-1" #12 prio=5 os_prio=31 tid=0x00007feae48f7000 nid=0xa203 waiting for monitor entry [0x000070001134e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.company.Main.test4(Main.java:83)
	- waiting to lock <0x000000071569c3b0> (a java.lang.Class for com.company.Main)
	at com.company.Main$2.run(Main.java:58)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

 

 

  • 스태틱메서드를 synchronized로 선언하여 다른 스레드가 Blocked된 상황

아래와 같이 시나리오를 코드로 구현해보았다.

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t1 sleep start");
                    test2();
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 sleep start");
                test1();
            }
        });
        t1.start();
        t2.start();
        
        
    public synchronized static void test1() {
        System.out.println("test1");
            try {
                System.out.println("test1 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
    }

    public synchronized static void test2() {
        System.out.println("test2");
        try {
            System.out.println("test2 sleep start");
            Thread.sleep(1000 * 60);
        }catch (Exception e){
            e.printStackTrace();

        }
    }

힙덤프를 확인해보면

Thread-0에서 Main 클래스에 대해 모니터락을 소유하고 있고

Thread-1에서 Main 클래스의 모니터락을 기다리고 있는 것을 확인할 수 있다.

 

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f814306c800 nid=0xa403 waiting on condition [0x000070000ebff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.Main.test2(Main.java:98)
	- locked <0x000000071569c3e0> (a java.lang.Class for com.company.Main)
	at com.company.Main$1.run(Main.java:37)

   Locked ownable synchronizers:
	- None


"Thread-1" #12 prio=5 os_prio=31 tid=0x00007f8153822000 nid=0xa303 waiting for monitor entry [0x000070000ed02000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.company.Main.test1(Main.java:85)
	- waiting to lock <0x000000071569c3e0> (a java.lang.Class for com.company.Main)
	at com.company.Main$2.run(Main.java:52)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

 

 

 

 

 

 

+ 추가

스태틱메서드 안에서 synchronized 블럭으로 묶여있는 경우 다른 클래스에 대해서 Lock을 걸면
동기화 되지 않는다. (당연한 이야기이지만)

아래와 같이 서로 다른 클래스에 대해 락을 걸도록 변경한다.

    public static void test3() {
        System.out.println("test3");
        synchronized (Main.class){
            try {
                System.out.println("test3 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public static void test4() {
        System.out.println("test4");
        synchronized (Sub.class){
            try {
                System.out.println("test4 sleep start");
                Thread.sleep(1000 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

아래의 스레드덤프를 보면 동기화 되지 않고 각각 TIMED_WAITING 상태임을 확인할 수 있다. 

"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fcf03815000 nid=0x5a03 waiting on condition [0x000070000fc2e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.Main.test4(Main.java:84)
	- locked <0x0000000715fca4a8> (a java.lang.Class for com.company.Sub)
	at com.company.Main$2.run(Main.java:58)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fcf048b0800 nid=0x5903 waiting on condition [0x000070000fb2b000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.Main.test3(Main.java:72)
	- locked <0x000000071569c3e0> (a java.lang.Class for com.company.Main)
	at com.company.Main$1.run(Main.java:41)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

 

 

스태틱메서드 안에서 synchronized 블럭으로 묶여있는 경우 다른 클래스에 대해서 Lock을 걸면
동기화 되지 않는다.

아래와 같이 서로 다른 Object에 대해 락을 걸도록 변경한다.

        Object object = new Object();
        Object object2 = new Object();

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
	            testSync.test3(object);
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                testSync.test4(object2);
            }

        });
        t1.start();
        t2.start();



class TestSync{
    public  void test3(Object object) {
        System.out.println("test3");
        synchronized (object){
            try {
                System.out.println("test3 sleep start");
                Thread.sleep(1000 * 60 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public  void test4(Object object) {
        System.out.println("test4");
        synchronized (object){
            try {
                System.out.println("test4 sleep start");
                Thread.sleep(1000 * 60 * 60);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

아래의 스레드덤프를 보면 동기화 되지 않고 각각 TIMED_WAITING 상태임을 확인할 수 있다.

"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fd9a1009800 nid=0xa103 waiting on condition [0x0000700001b11000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.TestSync.test4(Main.java:164)
	- locked <0x000000071569ed50> (a java.lang.Object)
	at com.company.Main$2.run(Main.java:58)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fd975812800 nid=0xa203 waiting on condition [0x0000700001a0e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.TestSync.test3(Main.java:153)
	- locked <0x000000071569ed40> (a java.lang.Object)
	at com.company.Main$1.run(Main.java:41)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

'개발 > Java' 카테고리의 다른 글

java.util.collections 총정리 set편  (0) 2021.05.13
Lombok 이란?  (0) 2021.05.12
mixin이란  (0) 2021.05.11
hashCode()  (0) 2021.05.08
힙덤프  (0) 2021.04.16