자바에서 멀티 스레드를 잘 사용하면 좋은 성능을 내는 프로그램을 개발할 수 있지만, 스레드 간 동기화 문제를 필수적으로 해결해야 합니다.
예를 들어 스레드 간 공유 자원으로 사용하고 있는 데이터가 있을 경우, 여러 개의 스레드가 하나의 데이터에 접근할 경우 신뢰성을 보장할 수 없게 됩니다.
이러한 스레드 동기화 문제는 자바 heap영역에서 발생하게 됩니다. 왜냐하면 자바 스레드는 스레드 안에 스택 영역을 포함하고 있기 때문에 스택 영역의 경우에는 Thread-Safe 하게 되지만 heap영역 같은 경우에는 공유하는 메모리 영역이기 때문에 Thread 간에 safe하지 못하게 됩니다.
이러한 동기화 문제를 해결하기 위해서 java에서는 thread-safe를 지원하는 synchronized키워드를 사용하여 스레드 간 동기화를 시켜주게 됩니다. synchronized 키워드를 사용하게 되면 여러 개의 스레드가 하나의 공유 자원에 접근하게 될 때, 현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들은 데이터에 접근할 수 없게 됩니다.
synchronized 키워드는 변수와 메서드에 사용하여 동기화를 할 수 있습니다. 하지만 무분별한 동기화는 오히려 프로그램의 성능 저하를 일으킬 수 있으므로 유의하여 사용해야 합니다.
synchronized를 사용하는 방법은 아래와 같습니다.
// 메서드에 사용하는 경우
public synchronized void method() {
// 코드
}
// 객체에 사용하는 경우
private Object obj = new Object();
public void exampleMethod() {
synchronized(obj);
}
자바 synchronized 키워드는 네 가지 유형의 블록에서 사용됩니다.
- 인스턴스 메서드
- 스태틱 메서드
- 인스턴스 메서드 코드 블록
- 스태틱 메서드 코드 블록
인스턴스 메서드 동기화
public synchronized void add(int value) {
this.count += value;
}
인스턴스 메서드의 동기화는 해당 키워드가 존재하는 메서드의 동기화를 의미합니다. 인스턴스 메서드의 동기화는 해당 메서드를 가진 인스턴스를 기준을 이루어지기 때문에, 한 클래스가 동기화된 인스턴스 메서드를 가진다면, 여기서 동기화는 이 클래스의 한 인스턴스를 기준으로 이루어집니다. 따라서 한 시점에 오직 하나의 스레드만이 동기화 인스턴스 메서드를 실행할 수 있게 됩니다.
스태틱 메서드 동기화
public static synchronized void add(int value){
count += value;
}
스태틱 메서드의 동기화는 인스턴스 메서드와 같은 방식으로 이루어집니다. 스태틱 메서드의 동기화는 이 메서드를 가진 클래스의 클래스 객체를 기준으로 이루어집니다. JVM안에 클래스 객체는 클래스 당 하나만 존재할 수 있으므로 같은 클래스에 대해서는 오직 한 쓰레드만 동기화된 스태틱 메서드를 실행할 수 있게 됩니다.
인스턴스 메서드 안의 동기화 블록
동기화가 반드시 메서드 전체를 대상을 이루어져야 하는 것은 아닙니다. 종종 메서드의 특정 부분에 대해서만 동기화를 하는 편이 효율적인 경우가 존재합니다. 이와 같은 경우에는 메서드 안에 동기화 블록을 만들 수 있습니다.
public void add(int value) {
synchronized(this) {
this.count += value;
}
}
이와 같이 메서드 안에 동기화 블럭을 따로 설정할 수 있습니다. 하지만 이 예제는 메서드 안의 동기화 블록밖에 다른 코드가 존재하지 않지만, 메서드 선언부에 synchronized를 사용한 것과 같은 기능을 합니다.
여기서 동기화 블록이 파라미터로 this를 받고 있는 것을 주목해야 합니다. 여기서 this는 이 add() 메서드가 호출된 객체를 의미하게 됩니다. 이 동기화 블록 안에 전달된 객체를 모니터 객체라고 부르게 됩니다. 즉 이 코드는 이 모니터 객체를 기준으로 동기화가 이루어지고 있음을 나타냅니다. 그러므로 같은 모니터 객체를 기준으로 동기화된 블록 안의 코드는 오직 한 스레드만이 실행할 수 있게 됩니다.
스태틱 메서드 안의 동기화 블럭
스태틱 메서드 안의 동기화 블록은 각 메서드를 가지고 있는 클래스 객체를 기준으로 동기화를 지원합니다.
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
위와 같은 코드가 있을 경우, 같은 시점에 오직 한 스레드만 위의 두 가지 메서드 중 하나를 실행할 수 있습니다.
하지만 만약 두 번째 동기화 블록의 괄호에 MyClass.class가 아닌 다른 객체를 전달하게 된다면 스레드는 동시에 각 메서드를 실행할 수 있게 됩니다.
'Language > Java' 카테고리의 다른 글
[Java] JVM의 Runtime Area (0) | 2020.01.13 |
---|---|
[Java] Lombok 사용하기 (0) | 2019.11.15 |
[Java] JVM 가상머신과 Java Language의 이식성, 호환성 관계 (0) | 2019.11.10 |
[Java] Garbage Collector란? (0) | 2019.11.10 |
[Java] JSP와 Servlet이란? (0) | 2019.10.13 |