프로세스? 쓰레드?
프로세스란 실행 중인 하나의 애플리케이션을 의미하고, 쓰레드란 한 프로세스 내에서 동작하는 여러 실행 흐름을 의미한다.
여러개의 쓰레드가 동시에 작동하는 것이 멀티 쓰레드이며, 멀티 쓰레드를 사용할 때에는 동기화(Synchronization)나 교착상태(Deadlock)와 같은 문제들이 발생할 수 있다.
여러개의 쓰레드가 자원을 공유해서 사용해도 괜찮을 수 있지만, 심각한 오류를 발생시키는 경우가 있다.
이럴 때 쓰레드들이 자원에 동시에 접근하는 것을 막기 위해서 synchronized라는 키워드를 사용해서 쓰레드들의 동기화 문제를 해결할 수 있다.
그러나 synchronized 키워드는 프로그램 성능에 굉장히 악영향을 미친다.
ThreadLocal
ThreadLocal은 자바의 class이다. 각각의 쓰레드들에게 고유한 저장소를 제공하는 변수라고 생각할 수 있다.
두 개(혹은 그 이상)의 쓰레드가 하나의 ThreadLocal 변수를 참조하더라도, 서로 사용하는 ThreadLocal이 달라지는 것이다.
즉 멀티 쓰레드 환경에서 각 쓰레드마다 독립적인 변수를 사용할 수 있게 해주는 것이다.
또한 쓰레드가 같다면, 어디서든 참조하여 사용할 수 있게된다.
사용법
ThreadLocal<T> local = new ThreadLocal(); //생성
local.set(T); //값 할당
local.get(); // 값 사용
예시는 다음과 같다.
public class ThreadLocalService {
private ThreadLocal<String> nameStore = new ThreadLocal<>();
public String logic(String name){
System.out.println(String.format("저장 name = %s -> nameStore = %s", name, nameStore.get()));
nameStore.set(name);
sleep(1000);
System.out.println(String.format("조회nameStore = %s", nameStore.get()));
return nameStore.get();
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadLocalServiceTest {
private ThreadLocalService service = new ThreadLocalService();
@Test
public void local() throws Exception {
//given
Runnable userA = () ->{
service.logic("userA");
};
Runnable userB = () ->{
service.logic("userB");
};
Thread threadA = new Thread(userA);
threadA.setName("thread-A");
Thread threadB = new Thread(userB);
threadB.setName("thread-B");
threadA.start();
threadB.start();
sleep(3000);
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
만약 ThreadLocal이 아닌 일반 String 필드였다면, 동시성 문제가 발생하여 결과는 다음과 같았을 것이다.
그러나 ThreadLocal을 사용한다면 결과는 다음과 같다.
주의사항
스레드 로컬은 사용 후 보관된 데이터를 반드시 삭제해 주어야 한다.
그렇지 않다면 재사용 되는 쓰레드가 올바르지 않은 데이터를 참조하여 오류를 발생시킬 수 있다.
제거는 다음 메소드를 사용한다.
ThreadLocal.remove()
다음과 같이 사용한다.
public class ThreadLocalService {
private ThreadLocal<String> nameStore = new ThreadLocal<>();
public String logic(String name){
try {
System.out.println(String.format("저장 name = %s -> nameStore = %s", name, nameStore.get()));
nameStore.set(name);
sleep(1000);
System.out.println(String.format("조회nameStore = %s", nameStore.get()));
return nameStore.get();
}finally {
nameStore.remove();
}
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
📔 Reference
'☕️ Java > 기본' 카테고리의 다른 글
자바는 어떻게 작동하는가? (JVM, Class Loader, JVM Memory 등) (0) | 2022.01.24 |
---|---|
[Java] 자바 JVM 메모리 관리 파헤치기 (스택, 힙, Reference Type, 가비지 컬렉터) (0) | 2022.01.24 |
[Java] 애너테이션에 대한 기초 (0) | 2022.01.22 |
[Java] HashSet, LinkedHashSet, TreeSet (0) | 2022.01.21 |
[Java] 슈퍼 타입 토큰 (0) | 2022.01.03 |