본문 바로가기

Java Programming

Multi-Threading ⑤ Synchronization & DeadLock

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
병행성의 문제를 해결하기 위해서는 '동기화' 라는 키워드를 사용합니다. 쉽게 말하자면, 동기화를 한 메소드에는 한 스레드 객체만이 접근할 수 있도록 자물쇠와 열쇠를 놓습니다. 한 스레드가 메소드에 접근을 하고 열쇠를 가지고 자물쇠를 잠근후에 들어갑니다. 해당 스레드가 동기화된 메소드에서 나오기 전까지는 키가 반납되지 않으므로 이후에 도착한 스레드들은 먼저 들어간 스레드가 나올때 까지 기다립니다. 이와 같은 방식을 동기화 한다고 합니다.

메소드명 앞에 Synchronized 라는 키워드를 통해서 동기화를 시켜 줄수 있습니다. 따라서 지난 예제에서 말썽을 부리게 된 increase() 라는 메소드 앞에 synchronized 키워드를 사용하면 문제를 해결할 수 있습니다.

public synchronized void increase() {

            int index = total;

            System.out

                          .println(Thread.currentThread().getName() + " 스레드가 일을 시작합니다!");

            total = index + 1;

      }

그럼 이와 같은 질문을 할 수 있습니다. "스레드를 사용할 때에 안전하도록 전부 다 동기화 하면 안되나요?" 병행성의 문제점에 대해서 골머리를 앓기보다는 모든 메소드에 동기화를 사용하여서 이런 문제를 근본적으로 해결할 수 있지만, 이렇게 되면 멀티 스레드를 사용하는 의미가 사라지게 됩니다. 멀티스레딩의 특징은 여러 스레드들이 작업을 수행하면서 퍼포먼스에 비약적인 발전을 주는 것이 목적인데 반해서, 이렇게 되면 단일 프로세스로 실행이 되는 것이나 다름이 없기 때문에, 성능상의 하자가 발생하게 됩니다. 또한 데드락이라는 문제점이 발생 되기도 합니다.

따라서 동기화 키워드를 사용할때에는 꼭 필요한 부분만 동기화를 하도록 하고 메소드에 무조건 동기화 키워드를 사용하는 것이 중요한것이 아니라, 특정 부분에서 동기화가 필요할 경우 따로 동기화 블럭을 통해서 동기화를 해주는 것이 좋습니다.

데드락이라는 문제점에 대한 소스코드를 작성 해보았습니다. (Thanks for Zerry)

public class DeadLockTest {

      public static void main(String[] args) {

            Man boy = new Man();

            Woman girl = new Woman();

            boy.setWoman(girl);

            girl.setMan(boy);

            Thread th1 = new Thread(girl, "남자");

            Thread th2 = new Thread(boy, "여자");

            th1.start();

            th2.start();

      }

}

class Man implements Runnable {

      Woman girl;

      public synchronized void sayLove() {

            String name = Thread.currentThread().getName();

            System.out.println(name + " 사랑을 전합니다.");

            System.out.println(name + " 잠시 기다립니다.");

            try {

                   Thread.sleep(1000);

            } catch (InterruptedException e) {

                   e.printStackTrace();

            }

            System.out.println(name + " 대답을 하길 기대할 겁니다.");

            girl.sayLove();

      }

      public void setWoman(Woman girl) {

            this.girl = girl;

      }

      public void run() {

            sayLove();

      }

}

class Woman implements Runnable {

      Man boy;

      public synchronized void sayLove() {

            String name = Thread.currentThread().getName();

            System.out.println(name + " 사랑을 전합니다.");

            System.out.println(name + " 잠시 기다립니다.");

            try {

                   Thread.sleep(3000);

            } catch (InterruptedException e) {

                   e.printStackTrace();

            }

            System.out.println(name + " 대답을 하길 기대할 겁니다.");

            boy.sayLove();

      }

      public void setMan(Man boy) {

            this.boy = boy;

      }

      public void run() {

            sayLove();

      }

}

실행을 시켰을때의 결과는 이렇습니다.

사용자 삽입 이미지

데드락이 걸린 현상입니다. Man과 Women 이라는 클래스는 서로 각자의 객체를 가지고 호출을 하지만, 서로의 sayLove() 라는 메소드에 락을 걸고 들어간다음에 서로 상대방의 키를 원하면서 무한히 기다리는 현상이 생겨나게 됩니다. 이렇게 되면, 자체적인 동작이 완전히 멈추는 상태가 되지요 ^^;

이처럼 무조건 동기화를 한다고 멀티스레딩의 문제점을 처리 할 수 있는것이 아니랍니다. 따라서 동기화가 꼭 필요한 곳에서만 동기화를 사용하도록 하고, 멀티스레딩의 병행성의 장점을 활용하는것이 중요하다고 볼 수 있습니다.