반대로 이전 작업의 완료 여부와 상관없이 바로 다른 작업 명령을 수행하는 것을 '비동기화' 라고 한다.
멀티 쓰레드 환경에서의 비동기
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 이 반납될 때까지 대기하게 된다.
프로세스는 최소 하나 이상의 쓰레드로 실행되며 자바의 경우 실행 시점에 main 쓰레드를 생성하여 작업을 실행한다.
쓰레드의 동시성
쓰레드의 동시성은 동시에 실행되는 것이 아닌 동시에 실행되는 것처럼 보이게하는 현상이다.
쓰레드를 실행하는 주체는 CPU 의 코어로서 코어의 수가 쓰레드의 수보다 작을 경우 코어는 쓰레드를 번갈아가면서 실행된다. 이 때, 번갈아가면서 실행되는 주기는 사람이 인지할 수 없을 정도로 매우 짧아 사용자는 마치 동시에 실행되는 것처럼 느낀다. 이를 쓰레드의 동시성이라고 한다.
쓰레드의 병렬성
CPU 의 코어 수가 쓰레드의 수 이상일 때 각각의 코어는 하나의 쓰레드를 담당하여 실행, 즉 병렬적으로 처리한다.