자바가 주로 웹서비스의 서버 프로그램에 많이 사용되는데 웹서비스는 기본적으로 multi- threading 을 지원하기 때문에 실제적으로 thread 프로그래밍을 자주 구현하지는 않습니다.
그런데 안드로이드 앱 프로그래밍에서는 thread 프로그램을 만들 일이 종종 있기는 합니다.
UI 와 독립적인 서비스를 생성하여 돌리거나 통신을 하기위해서(이 역시 서비스 이긴 합니다) thread를 생성하여 run() 하고 메세지를 post 하곤 합니다. 복잡한 이야기를 먼저 했는데요, 일딴 thread의 정의 부터 살펴보겠습니다.


프로그램이란 컴퓨터에게 일을 시키기 위한 명령어의 일련의 집합입니다. 하드에 설치되어있는 프로그램을 우리가 실행을 하면 메모리에 올라가게 됩니다. 이런 상태를 우리는 프로세스라고 합니다. 따라서 하나의 프로그램이 메모리에 올라가면 하나의 프로세스 상태가 됩니다. 그런데 프로세스는 CPU에서 실행되는 단위는 아닙니다. CPU를 점유하는 상태가 되려면 thread 가 생성되어야 합니다. 즉 CPU를 점유하고 실행되는 단위를 thread 라고 합니다.
그럼 하나의 프로그램이 실행된다면 당연히 하나 이상의 thread 상태가 됩니다. 이때 특별히 thread를 생성하는 코드가 없다면 이는 단일 쓰레드(single thread) 프로그램입니다.
그런데, 두 개 이상의 작업이 동시에 실행되는 듯한 효과가 필요할 수 있습니다.
앞에서 안드로이드 예를 들었듯이 화면(UI)가 있는 상태에서 다운로드를 받는다거나 음악이나 영상을 플레이를 하면 화면이 제어되는 기능과  다운로드 하는 기능은 서로 다른 thread로 구현되어야 합니다.
플레이나 다운로드가 지속적으로 되는 순간에 UI 입력을 받을수는 없기 때문이죠
이런 경우 두 개의 작업이 마치 동시에 일어나는듯한 효과가 필요한데 이를 다중쓰레드 (multi-thread) 프로그램이라고 합니다.
(컴공과 대학 3년차 정도에 OS(Operating System) 이라는 과목을 배우는데 여기서 thread에 대한 이야기를 매우 자세히 배웁니다.)
그럼 자바에서는 thread를 어떻게 생성할까요? 

자바에서는 thread를 구현하는 방법은 크게 두 가지가 있습니다.
Thread 클래스에서 상속을 받는 경우와 Runnable 인터페이스를 구현하는 방법입니다.
먼저 Thread 클래스를 상속받는 방법부터 보겠습니다. 
MyThread 라는 클래스를 만들고 Thread 클래스에서 상속을 받습니다.
그리고 Test 클래스에서 두 개의 MyThread를 생성하여 실행합니다. 

출력 결과의 일부는 다음과 같습니다.

Thread를 상속받은 MyThread는 반드시 run() 메서드를 정의해야 합니다. 이 메서드는 start() 메서드가 호출하는 메서드로써 thread 가 실행될 때 하는 작업을 구현합니다. 여기서는 0부터 200까지의 수를 출력하도록 합니다.
출력 결과를 보았을 때 ThreadTest에서 실행된 쓰레드는 총 몇개인가요???
3개 입니다. “start” 를 출력하고 MyThread 두 개를 생성하여 실행하고 “end”를 출력하는 main thread 가 하나 있고  두 개의 MyThread 가 있습니다.
출력결과를 보면 main thread 가 가장 먼저 끝났고 MyThread 두 개가 번갈아 가며 숫자를 출력하는 것을 알 수 있습니다.

이번에는 Runnable 인터페이스를 구현한 코드를 살펴보겠습니다.

Runnable 인터페이스는 반드시 run() 을 구현해야 합니다.
그리고 Runnable 인터페이스를 구현한 클래스 자체는 쓰레드 클래스가 아니므로 Thread 클래스의 생성자에 매개변수로 넣어 생성한 후 start() 를 호출합니다.
만약 한번 쓰고 버릴 thread 인 경우 익명클래스로 생성할 수 있는데 다음과 같이 만들수 있습니다. 

여기까지는 자바에서 어떻게 쓰레드 프로그램을 구현하는가에 대한 설명이었습니다. 

그런데, thread 프로그래밍에서 가장 중요한 부분은 동기화(synchronization)를 어떻게 구현하느냐 하는 문제입니다. 동기화를 이야기 하기 전에 thread의 상태에 대해 잠시 알아야 합니다.
thread 가 생성되고 start가 되면 실행 가능한 상태가 됩니다. 이를 runnable 상태라고 합니다.
그리고 thread가 CPU를 점유하여 실행하게 되면 run 상태가 됩니다. 
thread 가 실행을 다 마치고 종료되면 dead 상태라고 합니다.여기까지는 thread가 생성되고 실행되고 종료되는 일반적인 과정입니다. 
그런데 가끔 thread 가 CPU를 점유할 수 없는 상태로 빠지게 되는 경우가 있습니다.
이를 not runnable 상태 라고 합니다. thread가 not runnable 상태가 되면 다시 runnable 상태가 되기 전에는 CPU를 점유할 수 없습니다. 즉, 실행될 수 없다는 뜻입니다.
위에서 설명한 상태를 그림으로 간단히 나타내면 다음과 같습니다.

자 그럼 자바에서 thread는 어떤 경우에 not runnable 상태가 되는 걸까요? 그림에서 보듯이 세 가지 경우에 의해 not runnable 상태가 됩니다.

첫번째는 일정시간 thread를 잠시 멈추게 하는 Thread.sleep() 메서드를 사용하는 것입니다. 이렇게 되면 지정한 일정 시간이 지난 후면 thread는 다시 runnable 상태가 됩니다. 
두번째는 thread를 잠시 기다리에 하는 것입니다. 이를 wait() 메서드를 사용할 수 있습니다. 참고로 wait() 메서드는 Thread 클래스의 메서드가 아니라 Object 클래스의 메서드 입니다. 기다리게 된 thread는 다시 깨워줄때 까지(awake up) 기다리게 되는데 이때 깨워주는 메서드는 notify()나 notifyAll() 입니다. 여기서 사용하는 notify()와 notifyAll() 메서드 역시 Object의 메서드 이고 이 메서드들은모두 final로 선언되어 재정의 할 수 없습니다. 
마지막으로 Thread 클래스에서 제공되는 join() 메서드가 있는데 join()을 사용하게 되면 join()을 호출한 다른 thread가 끝날 때 까지 내 thread가 not runnable 상태가 됩니다.

(동기화가 중요하다고 해 놓고 이렇게 thread 상태를 열심히 설명하는 이유는 동기화를 구현하기 위해 thread를 잠시 쉬게 해야 하고 이때 wait() 과 notify(), notifyAll() 메서드가 사용되기 때문입니다. )
sleep(time) 메서드는 Thread의 static 메서드로서  1/1000 초 단위의 값을 넣어서 호출합니다. 가령 Thread.sleep(1000); 이라고 쓰면 1초 동안 thread가 not runnable 상태가 되는 것입니다. 

자 이게 wait() 메서드가 호출되는 경우를 생각해 봅시다.
두 개의 thread가 동시에 공유 메모리에 접근 한다고 가정해 봅시다.
이때 이 공유 리소스를 우리는 critical section(임계영역) 이라고 합니다.

임계역영에 하나의 thread가 작업을 시작하게되면 그 영역에 대한 작업이 끝날때까지 다른 thread가 접근하지 않아야합니다. 이것이 동기화의 기본적인 방법입니다. 우리가 OS 에서 배우는 세마포어나 모니터등은 이러한 동기화를 구현하기 위한 방법들인것이죠. 만약 이러한 공유리소스에 대한 하나의 thread의 메서드 수행이 마무리 되기 전에 다른 thread의 메서드가 수행된다면 어찌될까요?

아주 간단한 예로 다음을 살펴보겠습니다.
static int num = 1; 

이라는 변수가 있는데 ( 아시겠지만 static 변수는 하나의 메모리를 공유합니다)
하나의 thread-A는 num값을 가져와서 10을 더해주고 
다른 thread-B는 num값을 가져와서 2를 빼준다고 합시다

thread- A 가 하는 일을 간단히 코드로 나타내면
int value = num;               –    1
num = value + 10;            –    2

thread-B 가 하는 일을 간단히 코드로 나타내면
int value = num;              –     3
num = value -2;              –      4

두 개의 thread 가 동시에 작업이 될 때 가장 이상적인 경우는 1, 2, 3, 4 의 순서대로 명령이 진행되는 것이고 그럼 결과는 1+10 -2 가 되므로 9 이어야 합니다.
그런데 동기화가 구현되지 않으면 즉, thread-A의 num에 대한 작업이 끝나기 전에 thread-B의 작업이 이루어 진다면 이런 경우가 생길 수 있습니다.  1번 작업이 실행되고 나서 (value 가 1인 상태에서) 2번을 실행하기 전에 thread-B 가 CPU를 점유하면 3번이 실행됩니다. 그리고 4번이 실행되어 num 의 값은 -1 이 됩니다. 
그후 thread-A가 다시 CPU를 점유해서 2번 작업이 실행되는데. 여기서는 이전에 가져온 value 값이 아직 1 이기 때문에 num 의 결과는 11 이 됩니다. 
다시 명령의 순서를 보면  1, 3, 4, 2 이렇게 처리가 된 경우가 발생할 수 있습니다. 
이런 문제가 발생하는 이유는 critical section에 대해 동기화(synchronization) 이 구현되지 않았기 때문입니다. 
간단하게 동기화는 순서를 정하는 것이라 생각하면 됩니다.
공유하는 리소스를 여러 thread 가 동시에 접근하더라도 순서를 정하여 공유 리소스에 대한 접근이 이루어지도록 합니다. 이렇게 하려면 thread-A 나 thread-B 가 먼저 수행되었을 때 나머지 thread는 기다려야 합니다. (다른 말로, 공유 리소스에 다른 thread가 접근하지 못하도록 lock을 건다라고 이야기 합니다. ) 자바에서는 이때 사용하는 메서드가 wait() 입니다. 그리고 기다리는 thread 에게 이제 리소스가 available 한 상태이니 다시 깨어나라고 알려주는 메서드가 notify()와 notifyAll() 메서드 입니다. 

자바에서는 synchronized 메서드와 synchronized block 방식으로 동기화에 대한 구현을 제공하고 있습니다. 

그럼 다음 포스트에서 자바에서 동기화 구현의 예에 대해 살펴보겠습니다.