Synchronized Block, Synchronized method
동기화(Synchronized)
- 공유 데이터(나머지 synchronized 된부분) 에 lock을 걸어 작업중이던 쓰레드가 마칠때까지 다른 쓰레드에게 제어권이 넘어가지 않게 보호한다.
- 교착상태에 주의해야 한다.
쓰레드는 같은 프로세스 내 데이터를 공유하기 때문에 일관성에 영향을 끼칠 수 있다.
만약 공유하는 자원을 처리하던 중 다른 쓰레드가 접근하기라도 한다면 ?
즉 동기화를 통해, 위와 같은 상황을 방지하고 어느 한 시점에 하나의 쓰레드만 접근하여 자원을 선점하고 프로그래머는 이를 효율적으로 관리할 필요가 있다.
예를 들어, 작업 중인 문서에 누군가 들어와 편집을 해서 내가 했던 자료가 날라갔다고 가정하면 큰 낭패 일 것이다. 작업중인 문서에 lock을 걸어 다른 사람의 편집권한을 막는 것과 같은 이치라고 생각하면 된다.
ex) Singleton Pattern의 인스턴스를 여러 객체가 접근 할 때, Static으로 정의된 객체를 여러 객체가 접근 할 때, 하나의 객체에 여러 개의 쓰레드가 접근하려고 할 때..
사용법
(1) 특정 객체 인스턴스에 lock을 걸려고 할 때 - synchronized block
private Worker wokrer = new Worker();
public void work(){
synchronized(worker){
//input
}
}
(2) 메소드에 lock을 걸 때 - synchronized method
public synchronized work(){
//input code
Worker worker = new Worker();
}
특정 인스턴스의 사용을 동기화 할 것이냐? 혹은 메소드 자체를 동기화 할 것이냐?의 구분의 차이로 인식하면 쉬울 것 같다.
사소하지만 주의깊게 생각해야 할 점이 있다면, (1) 번의 경우 synchronized 된 부분이 임계 영역이라면, (2) 번의 경우 메소드 전체가 임계영역이 된다는 점이다.
Sample Code
class Worker {
private Object obj;
private int count=0;
public Worker() {
}
public synchronized void writeData(Object obj) {
this.obj = obj;
count++;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
@Override
public synchronized String toString() {
return "Worker [obj=" + obj + ", count=" + count + "]";
}
}
public class Main {
public static void main(String[] args) {
//Worker라는 하나의 인스턴스를 공유할 것이다.
Worker worker = new Worker();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
worker.writeData("Writer1");
System.out.println("Data Print :"+worker.toString());
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
worker.writeData("Writer2");
System.out.println("Data Print :"+worker.toString());
}
}
});
thread1.start();
thread2.start();
}
}
- WriteData 메소드와 toString 메소드를 동기화함에 따라 순차적인 Count 증가 및 Data를 Write할 수 있었다.
=>결과 출력
위의 방법처럼 synchronized 된 메소드를 구성했다면 , 객체를 synchronized 하여 구성하는 것도 쉽게 할 수 있을 것이다.