synchronized 키워드를 사용하는 전형적인 3가지 케이스에 대해서 테스트 코드를 작성해 보았다.
첫 번째는 메소드에 synchronized 키워드를 추가하여 메소드 lock을 걸어주는 방법
두 번째는 synchronized(객체)와 같이 객체의 레퍼런스 변수를 이용하여 lock을 걸어주는 방법
세 번째는 synchronized(this)와 같이 메소드 구현부에 자기 자신의 객체에 대한 lock을 걸어주는 방법
테스트는 Junit을 이용하여 구현하였고, Junit으로 Multithread test 코드 작성 시 Program Argument 영역에 아래 옵션을 추가해 줘야 한다.
실행할 class 선택 후 오른쪽 마우스 클릭 > Run As > Run Configurations…
Argument 탭으로 이동하여 Program arguments에 다음 옵션 추가
-keepalive
객체는 단 한 개의 쓰레드 lock을 취할 수 있다.
즉, synchronized 키워드 블락에 thread 접근 시 다른 여러 쓰레드들은 synchronized 키워드 블락에 접근할 수 없다.
간단하게 예를 들면 아래와 같이 3개의 메소드가 있을 때 1번 쓰레드가 test3() 메소드 블락에 진입하면 다른 쓰레드들은 test2(), test3() 블락에 접근하지 못하고 thread wait set에 들어가게 된다.
1번 쓰레드가 test3() 메소드를 빠져나오게 되면 wait set에 있는 쓰레드 중 한 개가 test2() 또는 test3() 메소드 블락에 접근 한다.
synchronized 키워드가 추가되어 있지 않은 test1()은 thread lock과 상관없이 언제든지 접근 가능하다.
test1() {}
synchronized test2() {}
synchronized test3() {}
첫 번째 테스트 코드
public class SyncTest { | |
private static SyncTarget target; | |
@BeforeClass | |
public static void init() { | |
target = new SyncTarget(); | |
System.out.println("init"); | |
} | |
@Test | |
public void threadLockTest1() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
target.test1(); | |
}; | |
}.start(); | |
} | |
} | |
@Test | |
public void threadLockTest2() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
target.test2(); | |
}; | |
}.start(); | |
} | |
} | |
@Test | |
public void threadLockTest3() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
target.test3(); | |
}; | |
}.start(); | |
} | |
} | |
public static class SyncTarget { | |
public void test1() { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test1(), threadName=" + Thread.currentThread().getName()); | |
} | |
public synchronized void test2() { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test2(), threadName=" + Thread.currentThread().getName()); | |
} | |
public synchronized void test3() { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test3(), threadName=" + Thread.currentThread().getName()); | |
} | |
} | |
} |
두 번째 테스트 코드
public class SyncTest2 { | |
private static SyncTarget target; | |
@BeforeClass | |
public static void init() { | |
target = new SyncTarget(); | |
System.out.println("inint"); | |
} | |
@Test | |
public void threadLockTest2() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
synchronized (target) { | |
target.test2(); | |
} | |
}; | |
}.start(); | |
} | |
} | |
@Test | |
public void threadLockTest3() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
synchronized (target) { | |
target.test3(); | |
} | |
}; | |
}.start(); | |
} | |
} | |
public static class SyncTarget { | |
public void test2() { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test2(), threadName=" + Thread.currentThread().getName()); | |
} | |
public void test3() { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test3(), threadName=" + Thread.currentThread().getName()); | |
} | |
} | |
} |
세 번째 테스트 코드
public class SyncTest3 { | |
private static SyncTarget target; | |
@BeforeClass | |
public static void init() { | |
target = new SyncTarget(); | |
System.out.println("inint"); | |
} | |
@Test | |
public void threadLockTest2() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
target.test2(); | |
}; | |
}.start(); | |
} | |
} | |
@Test | |
public void threadLockTest3() { | |
for (int i = 0; i < 5; i++) { | |
new Thread() { | |
@Override | |
public void run() { | |
target.test3(); | |
}; | |
}.start(); | |
} | |
} | |
public static class SyncTarget { | |
public void test2() { | |
synchronized (this) { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test2(), threadName=" + Thread.currentThread().getName()); | |
} | |
} | |
public void test3() { | |
synchronized (this) { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("test3(), threadName=" + Thread.currentThread().getName()); | |
} | |
} | |
} | |
} |