멀티스레드 프로그램은 여러 스레드에서 같은 리소스에 접근을 시도할 때 에러나 예측 불가능한 결과를 만들어 낸다.
그래서 동기화 메소드들을 사용해서 특정 시간에 하나의 스레드만 한 리소스에 접근하도록 만들어주어야 한다.
자바에서는 스레드를 만드는 방법과 동기화 블록을 이용해서 작업을 동기화해주는 방법을 제공한다. 자바의 동기화 블록은 synchronized 키워드로 지정할 수 있다. 자바 동기화 블록은 한 객체에 대해서 동기화되어 있다. 같은 객체에 동기화 된 모든 동기화 블록들은 한번에 한 스레드만 내부 코드를 실행하도록 한다. 다른 스레드들은 동기화 블록에 들어간 스레드가 블록을 나올 때 까지 계속해서 동기화 블록에 접속을 시도한다.
// 한 번에 하나의 스레드만 실행된다.
// sync_object는
// monitor 방식(wait-signal)으로 lock 된 객체에 대한 레퍼런스
// 이 코드는 모니터 오브젝트에 대해 동기화 되었다고 말할 수 있다.
synchronized(sync_object)
{
// 공유된 변수와 리소스들에 접근한다.
}
이 동기화는 모니터 방식으로 자바에서 구현되어있다. 단 하나의 스레드만 주어진 시간에 모니터를 소유할 수 있다. 스레드가 락을 취득하면, 모니터에 들어갔다고 말한다. 다른 모든 스레드들은 첫 스레드가 모니터를 나올 때까지 계속 모니터에 들어가는 것을 시도하는 상태로 멈추게 된다.
아래는 synchronized 를 이용한 멀티 스레딩 예시이다.
import java.io.*;
import java.util.*;
// 메시지를 보내는 클래스
class Sender
{
public void send(String msg)
{
System.out.println("Sending\t" + msg);
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
System.out.println("Thread interrupted.");
}
System.out.println("\n" + msg + "Sent");
}
}
// 스레드를 이용해서 메시지를 보내는 클래스
class ThreadedSend extends Thread
{
private String msg;
private Thread t;
Sender sender;
ThreadedSend(String m, Sender obj)
{
msg = m;
sender = obj;
}
public void run()
{
// 한 번에 하나의 스레드만 메시지를 보낸다.
synchronized(sender)
{
sender.send(msg);
}
}
}
// 드라이버 클래스
class SyncDemo
{
public static void main(String args[])
{
Sender snd = new Sender();
ThreadedSend S1 =
new ThreadedSend( " Hi " , snd);
ThreadedSend S2 =
new ThreadedSend( " Bye ", snd);
// 두 스레드를 실행한다.
S1.start();
S2.start();
// 스레드가 끝나는 것을 기다린다.
try
{
S1.join();
S2.join();
}
catch(Exception e)
{
System.out.println("Interrupted");
}
}
}
실행결과는 항상 동일하다.
위의 예시에서는 ThreadedSend.run() 메소드 안에서 Sender 객체에 동기화를 했다.
다른 방법으로는 Sender 객체의 Sender 객체의 send() 메소드 전체를 동기화 블록으로 만들어 버리는 방법이 있다.
class Sender
{
public synchronized void send(String msg)
{
System.out.println("Sending\t" + msg );
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
System.out.println("Thread interrupted.");
}
System.out.println("\n" + msg + "Sent");
}
}
메소드 전체에 동기화를 하지 않아도 된다. 메소드의 일부분만 동기화를 하는 것이 장려된다.
class Sender
{
public void send(String msg)
{
synchronized(this)
{
System.out.println("Sending\t" + msg);
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
System.out.println("Thread interrupted.");
}
System.out.println("\n" + msg + "Sent");
}
}
}