대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 동기화에 의한 Thread Deadlock
등록일 조회수 4698
첨부파일  

동기화에 의한 Thread Deadlock

㈜엑셈 컨설팅본부 /APM지원팀 한 승민



개요

Java 객체나 method 의 동시성 제어를 위한 동기화 기법을 잘못 사용하였을 때 나타나는 Thread 간의 Deadlock 으로 인한 Transaction 지연 상황에 대하여 살펴보도록 하자 . 멀티 쓰레 드 환경에서 공유 객체의 연산을 수행하는 블럭에 synchronized 를 사용하는 경우 해당 객체의 read, write 작업은 모두 직렬화 되어 맨 처음 동기화된 블럭에 접근한 Thread 의 작업이 끝나 기 전까지 다른 thread 는 동일 객체에 대해 쓰기는 물론 읽기 작업 조차도 수행 할 수 없는 상 태가 된다 . 이러한 제약 자체 만으로도 성능에 영향을 미치는 것은 구태여 설명하지 않아도 될 것이다 .

동기화 기법을 사용할 때 발생하게 되는 성능 이슈 중 가장 큰 문제가 바로 Thread 간 Deadlock 현상이다 . 공유 객체인 A 객체와 B 객체가 존재한다고 가정할 때 A, B 객체를 상호 교차 참조하는 연산을 수행하는 동기화 된 블록을 가진 두 개의 어플리케이션이 존재 할 때 Deadlock 이 발생하게 되는데 이 문제를 방지하기 위해서는 동기화 블록 내의 연산 수행 범위를 최소화 하고 , A,B 객체의 참조 순서가 같게 하거나 A,B 객체를 참조하는 별도의 객체를 생성하 여 사용하는 방향으로 로직의 수정이 필요하다 .

이 밖에도 Java5.0 부터 새로 지원하는 동기화 기법인 Lock Interface 를 사용하여 Deadlock 상황을 최소화 하고 성능 개선을 꾀할 수 도 있다 . 그러나 이 Lock Interface 의 사용도 Deadlock 의 발생을 원천적으로 막아줄 수는 없다 . D eadlock 은 동기화 기법을 잘못 활용하였 을 때 발생한다는 점에서 로직 설계 및 개발 시점에 소스 수정 단계에서 공유 객체에 대한 동기 화를 사용하는 어플리케이션에 대한 확실한 검증을 거치지 않은 것이 원인이 되는 경우가 대부 분이기 때문이다 . 역시 이 문제의 해결은 개발 단계에서부터 상호 교차 참조를 하지 않도록 로직 을 구현하는 방법이 유일하다 할 것이다 .

기본적으로 JAVA 에서 Thread 는 서로 다른 Thread 의 작업에 대하여 영향을 주지 않고 독립적 으로 수행이 가능하도록 구현 되어 있다 .



그림과 같이 각각의 Thread 는 A 객체의 모든 public Method 에 대해 중복 접근이 허용된다 . 이 는 WAS 의 목적인 Web Service 환경에서 다수의 요청을 효율적으로 처리하기 위한 목적에서 비롯된 것이다 . 즉 직렬처리에서 오는 한계점을 극복하기 위해 모든 Thread 가 각각의 요청을 별도로 수행할 수 있도록 병렬처리가 가능하게 구현 되어 있기 때문이다 .

그러나 이러한 WAS 환경에서도 직렬처리가 필요한 상황이 분명 존재하기 마련이다 . 단순히 저 장되어 있는 Data 를 보여주기 위한 연산이 아니라 Data 수정해야 하는 상황이 온다면 , 거기에 더불어 해당 Data 는 WAS 에 접근하는 모든 요청에 대해 유일성을 지니는 공유 Data 라면 문 제가 달라지게 된다 . 즉 동기화 기법이 적용되어야 하는 것이다 .

이러한 패턴을 Single Threaded Execution 이라 하는데 이 패턴을 구현하는데 가장 대표적으 로 이용되는 것이 Synchronized 기법이다 . Synchronized 로 선언된 객체나 Method, 또는 Method 내의 블록에 Thread 가 접근하기 위해서는 monitor 라고 불리는 Lock 을 획득해야 한 다 . 이러한 Lock 을 통해서 해당 Data 에 대해 유일성을 보장하고 직렬처리를 가능하게 하는 것 이다 .



B Method 가 Synchronized 로 지정된 경우 이전과는 다르게 1 번 Thread 가 B Method 의 수 행을 종료할 때까지 2 번 Thread 는 대기하게 된다 . 이는 B Method 에 monitor Lock 이 구현되 었기 때문으로 만약 B Meth od 가 매우 빈번하게 수행되고 수행 시간이 오래 걸리는 Method 라 면 Transaction 수행 성능에 매우 좋지 않은 영향을 줄 수 있다 . 그래서 일반적으로 Synchronized 로 선언하는 경우 해당 객체나 Method, 블록의 수행 범위를 최소화 할 것을 권장 하고 있다 .

Synchronized 기법에서 발생할 수 있는 또 한가지의 문제점이 바로 Deadlock 이다 . Deadlock 은 상호 배타적인 접근을 동시 수행하려 할 때 발생하는 현상인데 예를 들어 Thread 1 번이 Transaction 수행 중 A 객체에 대한 monitor Lock 을 잡은 상태에서 B 객체에 대해 monitor Lock 을 잡기 위해 접근하는 상황에서 Thread 2 번이 B 객체에 대해 monitor Lock 을 획득 하 고 연산을 수행하는 도중에 A 객체의 monitor Lock 을 잡기 위해 대기 하는 상황이 발생한다 . 이렇게 서로가 서로의 Lock 을 먼저 해제하기만을 기다리고 있는 상황이 바로 Deadlock 현 상이 다 .



Thread 가 Deadlock 상황에 빠지게 되면 서로 다른 쪽의 monitor Lock 을 획득하기 위해 무한 히 대기하게 된다 . 이러한 Transaction 이 빈번하게 수행 된다면 얼마 지나지 않아 가용 Thread 가 전부 소진되어 서비스 장애 상황에 놓이게 될 것이다 . Synchronized 를 사용해야만 하는 환 경에서 Deadlock 을 회피하기 위한 방법은 어찌 보면 굉장히 단순하고 쉽다 . Sy nchronized 로 동기화 된 자원에 대해 어떤 Application 이든지 항상 같은 순서로 참조하게 하면 되는 것이다 . 위의 상황에서 B, C Method 를 수행하는 모든 Application 이 B Method 수행 후에 C Method 를 수행하도록 짜여져 있다면 Deadlock 은 절대 발생하지 않을 것이다 .

이는 대부분의 Deadlock 이 로직의 설계 과정이나 실제 구현 단계에서의 착오로 발생하는 것이기 때문에 미연 에 방지가 가능함을 말하는 것이다 . 그리고 만약 발생하였다 하더라도 아래의 방법을 통해 어떠 한 Application 에 의해 발생되었는지 파악이 가능하다면 어렵지 않게 조치가 가능할 것이다 .


JVM Thread Dump

Thread Dump 는 특정 시간에 JVM 상에서 수행중인 Thread 의 상태정보에 대한 일종의 snapshot 으로 Transaction 이 지연되는 시점에 Thread Dump 를 받으면 그 당시 각 Thread 가 어떤 일을 수행하고 어떠한 상태에 있는지 파악할 수 있도록 많은 정보를 제공하고 있다 .

Thread Dump 를 받기 위해서는 UNIX/Linux : kill -3 Windows : Ctrl+Break

명령을 이용한다 . Dump 결과는 Console 에 출력되므로 Redirection 을 통해서 파일로 저장하 여 사용하도록 한다 .



위의 내용은 Thread Dump 중 한 Thread 의 정보만을 추려낸 것이다 .
각 항목에 대해 살펴보면

1. Thread Name : Thread 의 이름을 나타냄 . (WAS 벤더에 따라 보여주는 형식이 약간씩 다름 .)
2. Thread id : system 에서 부여된 Thread ID
3. Thread status : Thread 의 수행 상태 정보 (JVM 별로 다름 )
4. Stack Trace : 당시의 Thread 에서 수행중인 Class Method 수행정보

크게 네 영역으로 나뉘어 지는데 각 영역이 담고 있는 내용은 WAS 벤더와 JVM 종류 별로 약간 씩의 차이가 있으므로 기본적인 의미만 이해할 수 있으면 된다 . 위의 Thread Dump 내용을 대략적으로 해석하면 Dump 수행 당시 http1 - w19 라는 이름을 가 진 Thread ID 0x08e718b0 의 Thread 가 monitor entry 에 접근하기 위해 waiting 을 하고 있으며 수행중인 Class Method 는 sfairPkg.Dlock.run3 이고 0xeffc9ce8 객체의 monitor lock 을 획득하려 하는 중이 었음을 알 수 있다 .

이처럼 Thread Dump 를 이용하면 당시 WAS 의 모든 Thread 에 대한 상태 정보를 얻을 수 있 어 Transaction 지연의 원인을 찾는데 중요한 단서를 제공 받을 수 있으므로 Transaction 처리 가 늦어지는 시점에 1~2 초 간격으로 두 세 번 정도의 Dump 를 받아 놓으면 사후 분석에 유용 하게 활용할 수 있다 . 이러한 Thread Dump 는 기본적으로 텍스트 형식으로 받을 수 있는데 분 석하기에 쉽지 않게 되어 있기 때 문에 GUI 를 통해 보다 쉽게 분석이 가능하도록 하는 공개 프 로그램들을 사용하는 것이 좋다 . 이 툴들 가운데 쓰기에 간편한 Thread Dump 분석 전용 프로 그램인 ‘ SAMURAI ’ 의 사용법에 대하여 간략하게 설명하도록 하겠다 .

‘ SAMURAI ’ 는 “Yusuke Yamamoto” 라는 일본인이 제작한 Dump 분석 도구로 Thread Dump, GC Dump 등을 손쉽게 분석할 수 있도록 도와주는 프로그램이다 . ‘ SAMURAI ’ 의 홈페이지에 가서 프로그램을 직접 수행해 볼 수도 있고 PC 로 다운받아 java . jre samurai.jar 명령을 통해 수행할 수도 있다 .



프로그램을 실행하면 빈 화면이 나타나는데 사용법도 매우 간단하다 . 받아놓은 Thread Dump 텍스트 파일을 드래그 해서 올려놓거나 File>Open 메뉴에서 불러오기만 하면 된다 .



Thread Dump 파일을 읽어 들인 후 두 개의 Tab 이 생기는 데 그 중 Log Tab 은 Dump 의 내용 을 볼 수 있는 메뉴 이고 Thread Dumps 는 Dump 의 내용을 분석하여 Thread 의 상태를 테이 블의 형태로 보여주는 메뉴이다 .



화면의 좌측은 Thread List 를 보여주고 우측은 각 Thread 의 Dump 당시 상태를 색상을 통해 나타내고 있다 . Deadlock 은 해골그림으로 표시되어 알기 쉽게 표현하고 있다 .



결론

이처럼 문제 시점의 Thread Dump 는 Transaction 지연 원인 분석에 많은 단서를 제공해 주기 때문에 장애 발생시나 Transaction 지연 시에는 Dump 를 받아놓는 것이 관리 측면에서 매우 유 용할 것이다 . 이번에 소개한 ‘ SAMURAI ’ 프로그램은 간단하면서도 분석에 필요한 Thread 상태 정보를 알기 쉽게 표현해 주고 있다 . 이 밖에도 각 OS JVM 벤더 별로 자체 제공하는 유용한 분 석 프로그램들이 많이 있는데 이를 십분 활 용한다면 대부분의 성능 지연의 원인을 어렵지 않게 분석해 낼 수 있을 것이다 .



출처 : (주)엑셈

제공 : DB포탈사이트 DBguide.net