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 |