Java
Thread의 동기화 처리
RubyRabbit
2022. 9. 4. 22:22
동기화
- 이전 작업이 완전히 완료된 후 다른 작업을 수행하는 것.
- 반대로 이전 작업의 완료 여부와 상관없이 바로 다른 작업 명령을 수행하는 것을 '비동기화' 라고 한다.
멀티 쓰레드 환경에서의 비동기
class TestClass {
int data = 3;
void print(Thread thread) {
System.out.println(thread.getName() + " : " + data);
data += 1;
}
}
@Test
@DisplayName("쓰레드 비동기 테스트")
void testThreadAsync() throws InterruptedException {
TestClass testClass = new TestClass();
Thread thread1 = new Thread(() -> testClass.print(Thread.currentThread()));
thread1.setName("thread1");
Thread thread2 = new Thread(() -> testClass.print(Thread.currentThread()));
thread2.setName("thread2");
Thread thread3 = new Thread(() -> testClass.print(Thread.currentThread()));
thread3.setName("thread3");
Thread thread4 = new Thread(() -> testClass.print(Thread.currentThread()));
thread4.setName("thread4");
// 실행 순서가 보장되지 않음
thread1.start();
thread2.start();
thread3.start();
thread4.start();
Thread.sleep(2000);
System.out.println("최종결과 : " + testClass.data);
}
- 기본적으로 멀티 쓰레드 환경에서는 비동기적으로 작업을 수행하지만 이는 특정 상태값을 공유하는 작업에서 의도치않은 문제를 야기할 수 있으므로 주의해야한다. 때문에 웹 어플리케이션같은 멀티 쓰레드 환경에서는 서로 다른 요청을 담당하는 여러 쓰레드가 같은 상태값을 공유함으로서 발생할 수 있는 문제를 방지하기 위해 기본적으로 무상태성을 권장한다.
멀티쓰레드 환경에서의 동기화
class SyncClass {
int data = 3;
Object lock = new Object();
// printA 와 printB 는 같은 this 객체의 Lock 을 사용하므로 동시에 진행될 수 없다.
synchronized void printA(Thread thread) {
System.out.println(thread.getName() + " : " + data);
data += 1;
}
synchronized void printB(Thread thread) {
System.out.println(thread.getName() + " : " + data);
data += 1;
}
// callA 와 callB 내부의 동기화 블록은 같은 객체의 Lock 을 사용하므로 동시에 진행될 수 없다.
void callA(Thread thread) {
synchronized (lock) {
System.out.println(thread.getName() + " : " + data);
data += 1;
}
}
void callB(Thread thread) {
synchronized (lock) {
System.out.println(thread.getName() + " : " + data);
data += 1;
}
}
// printA 와 callA 내부의 동기화 블록은 다른 객체의 Lock을 사용하므로 동시에 진행 가능
}
- 메서드를 동기화할 때는 동기화하고자 하는 메서드의 리턴 타입 앞에 synchronized 키워드를 붙여준다.
- 일정 영역을 블록으로 동기화할 때에는 synchronized(object) {} 블록으로 감싸준다.
- 메서드 전체를 동기화하지 않고 메서드 내부의 필요한 부분만 한정해서 동기화를 적용할 때 동기화 블록을 사용한다.
- 객체마다 하나의 Lock 을 보유하고 있으며 해당 Lock 이 필요한 동기화 메서드 혹은 동기화 블록을 처리하는 쓰레드는 해당 Lock 을 가지고 작업을 처리하며 Lock 을 반납하기 전까지는 해당 Lock 이 필요한 다른 쓰레드들은 Lock 이 반납될 때까지 대기하게 된다.