안드로이드, 또는 자바 프로그래밍을 할 때 아주 높은 빈도로 등장하는 키워드 중 하나가 'synchronized' 입니다.
쓰레드(Thread)를 이용해서 프로그래밍을 할 때 동기화를 맞추기 위해서 사용하는 명령어죠.
무작정 synchronized를 남발하거나 사용하다보면 프로그램 성능이 저하되거나 오동작, 또는 오히려 락(Lock)이
발생되는 경우가 있는데, 놓치기 쉬운 synchronized의 개념에 대해 확실히 알고 넘어가야 될 것 같습니다.
public class SynchronizedTest
{
public synchronized void A()
{
System.out.println("Method A");
}
public synchronized void B()
{
System.out.println("Method B");
}
public void C()
{
System.out.println("Method C");
}
}
위와 같은 클래스가 있다고 가정할 때, 메소드 A와 메소드 B는 synchronized가 사용되어져 있습니다.
다수의 쓰레드를 사용하는 프로그램에서 class SynchronizedTest 의 인스턴스 내부의 메소드 A, B, C에 접근할 때,
메소드 A와 메소드 B는 동시에 진입이 불가능합니다.
쓰레드 1과 쓰레드 2가 있다고 가정하면 쓰레드 1이 메소드 A를 수행하는 동안, 쓰레드 2는 메소드 A와 메소드 B에
접근이 제한이 됩니다.
많이 착각하는 오류 중 하나가 쓰레드 1이 메소드 A를 수행할 때, 쓰레드 2는 메소드 A로의 접근만 제한이 되는 걸로
생각하는 경우가 있는데 그게 아니라 메소드 A와 메소드 B 모두 접근이 제한됩니다.
그 이유는 다음과 같습니다.
public synchronized void A()
{
System.out.println("Method A");
}
이렇게 메소드 전체에 synchronized가 걸린 경우는
public void A()
{
synchronized (this)
{
System.out.println("Method A");
}
}
이것과 같습니다. this, 즉 현재 클래스의 인스턴스를 이용해서 락(Lock)을 걸기 때문입니다.
그렇게 때문에 한 인스턴스 내에서 synchronized된 메소드들은 여러 개의 쓰레드에서 동시에 진입이 불가능합니다.
즉, 함부로 synchronized를 남발하다가는 상당히 심각한 성능 저하가 발생할 수 있습니다.
그리고 여기서 알 수 있는 또 다른 점은 락은 인스턴스마다 존재한다는 점입니다.
즉, class SynchronizedTest의 인스턴스를 여러 개 만들었다면, 각각의 인스턴스마다 락이 따로 존재하게 됩니다.
하지만, 만약 static을 이용해서
public static synchronized void A()
{
System.out.println("Method A");
}
가 된다면 이야기는 조금 달라집니다. 이 메소드는 각 인스턴스마다 생기는 고유의 메소드가 아니라
static으로 설정되어진 메소드이기 때문에
public void A()
{
synchronized (SynchronizedTest.class)
{
System.out.println("Method A");
}
}
이것과 같은 코드가 됩니다.
synchronized는 쓰레드 기반의 프로그래밍을 할 때 상당히 중요한 키워드이기 때문에
그 개념은 확실히 알고 넘어가야 할 것 같습니다.