본문 바로가기

Better SW Development

[dW Review] 자바 동시성 처리 학습 팁

저의 주 사용언어는 자바입니다. 자바 외에도 이런저런 언어를 사용했지만, 해당 언어가 가지는 미묘한 아키텍처적인 특징들을 찾는데 많은 시간을 쓰진 않았던 것 같습니다. 사실 자바언어도 아직 제대로 알려면 멀었다고 생각합니다.

특히 멀티쓰레딩/동시성 관련 부분은 봐도 봐도 어려운 부분인것 같습니다. 해당 분야를 프로그래밍시에 자주 쓰지 않았기 때문인 것도 그 이유중 하나인 듯 합니다. 예전에 Kent Beck에게 멀티쓰레드 영역의 TDD에 대해 물어봤던 적이 있습니다. 수 십년 프로그래밍 경험을 가진 그로부터도 사실 뾰족한 대답을 듣진 못했습니다. 우선은 예외로 하고 넘어간다는 식으로 답변을 들었던 기억이 납니다.

사실 절차적으로 쭈욱~ 흘러가면 끝나는 식으로 짜여진 프로그램을 놓고, 멀티 쓰레드환경의 경쟁조건(race condition)이란걸 따져보며 살펴보기 시작하면 골치 아픈게 한 두가지 아니라는 걸 알게 됩니다. 특히 동시성 문제의 경우 '에라 모르겠다 syncronized 걸어버리고 끝내자!'라고 마음먹고 대충 써버리면 프로그램이 참 후지게 동작하기 시작합니다.

[형! 그 정도는 내가 짤게! 이클립스 단축키정도는 나도 쓸 수 있다고! - 우리집 수동이]


사실 복잡한 프로그램이 아니면 몇 가지 원칙만으로도 어느정도까지 견뎌낼 수 있습니다.

- 멀티쓰레드/동시성 문제는 비용을 지불한 준비가 되어있기 전까진 직접 작업을 최대한 회피한다. (...)
- 객체의 상태저장을 최소화 하고, 가능하다면 immutable 객체로 만든다.

물론 관련 전문 서적을 읽는 것이 좋죠.



그럴 여유까지 되지 않는다면 최근 IBM DW에 나온 기사 중에서 이와 관련해서 쉽게 잘 설명해 주는 두 글을 읽는 것도 많은 도움이 됩니다.


멀티코어 시스템의 Java 동시성 버그 패턴
http://www.ibm.com/developerworks/kr/library/j-concurrencybugpatterns/index.html?ca=drs-

멀티스레드 프로그래밍에 대해 모르고 있던 5가지 사항
http://www.ibm.com/developerworks/kr/library/j-5things15/index.html?ca=drs-

특히 자바개발자의 50%만이 키워드를 알고 있으며 실제 사용법에 대해서는 10% 미만만이 알고 있다는 volatile 키워드에 대한 부분은 두 기사에서 모두 다루고 있으며, 상호 보완적으로 설명이 잘 되어있습니다.

i++ 이 실제로는 read - update - write의 세가지 원자성으로 구성되어 있다는 것을 아는것 만으로도 여러가지를 느끼게 해줍니다.

동시성에 관심이 있고 배우고 싶다면, 관련 책을 보는 것도 방법입니다만, 기초내용을 튼튼하게 학습하기에 좋은 곳으로는 자바 공식사이트만한 곳이 없습니다. 영어도 쉽고 예제도 많아서 생각보다 보기 쉽습니다.

그리고 IBM DW기사에도 언급되어 있지만 Double Checked Locking, Wait Not In Loop, Unconditional Wait, Mismatched Wait and Notify, Two Locks Held While Waiting 등등의 대표적인 동시성 버그 패턴들을 함께 학습하면 더 좋습니다.

static ClassA field;
static ClassA createSingleton() {
    if (field == null) {
        synchronized (lock) {
            if (field == null) {
                ClassA obj = new ClassA(); // obj초기화
                field = obj; //필드에 할당
            }
        }
    }
    return field;
}
[대표적인 버그패턴 Double Checked Locking]


느닷없는 깜짝 퀴즈!
다음 코드의 문제점은 무엇입니까? 또 어떻게 하면 개선 가능할까요?
간단한 문제지만 안에 담겨있는 메시지는 작지 않으니까 꼭 한번 고민해 보세요. : )
class HelloRun implements Runnable{
	@Override
	public void run() {
		System.out.println( ">>>" + Thread.currentThread().getName() + ": started");
		if( Thread.currentThread().getName().equals("one") ){
			stepA();	
		} else {
			stepB();
		}
	}

	private synchronized void stepB() {
			System.out.println("started B");
			System.out.println("Do something");
			System.out.println("end B");
	}

	private synchronized void stepA() {
			System.out.println("started A");
			System.out.println("Do something");
			System.out.println("end A");		
	}
	
	public static void main(String[] args) {
		HelloRun helloRun = new HelloRun();

		Thread t1 = new Thread(helloRun, "one");
		Thread t2 = new Thread(helloRun, "two");
		t1.start();
		t2.start();
	}
}