동시성 문제
- 동시성 문제는 지역 변수에서는 발생하지 않는다. 지역 변수에서는 쓰레드마다 각자 다른 메모리 영역에 할당되기 때문이다.
- 단순 조회작업이 아닌, 수정작업이 있을때 동시성 문제가 발생한다.
예제
- userA가 자신의 Key값을 keyA로 설정하고, userB는 자신의 Key값을 keyB로 설정하려 한다.
- userA가 자신의 key값을 keyA로 셋팅
- 1초후에 userA의 key = keyA로 출력하려 하지만
- 그 0~1초 사이인 0.1초 후에 userB가 key 값을 keyB로 셋팅해버림
- 그 결과 userA와 userB의 key값이 모두 keyB로 셋팅되어버림
package com.example.controller;
public class NormalCase {
private static String key = "";
public static void main(String[] args) throws InterruptedException {
Runnable defaultUserA = () -> {
try {
function("keyA");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable defaultUserB = () -> {
try {
function("keyB");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
System.out.println("NormalCase start");
Thread threadA = new Thread(defaultUserA);
threadA.setName("threadA");
Thread threadB = new Thread(defaultUserB);
threadA.setName("threadB");
threadA.start();
Thread.sleep(100);
threadB.start();
Thread.sleep(3000);
System.out.println("NormalCase End");
}
public static void function(String keyName) throws InterruptedException {
key = keyName;
Thread.sleep(1000);
System.out.println(keyName + " 의 key = " + key);
}
}
해결방법 ThreadLocal
- ThreadLocal이란 Thread 내부에서 사용되는 지역변수이다.
- 지역변수이므로 공유되지 않는다.
- 반드시 사용 후에 remove() 해줘야 한다.
예제
- 위와 동일하게 userA가 자신의 Key값을 keyA로 설정하고, userB는 자신의 Key값을 keyB로 설정하려 한다.
- userA가 자신의 key값을 localAkey = keyA로 셋팅
- 1초후에 userA의 localAkey = keyA로 출력하려 함
- 그 0~1초 사이인 0.1초 후에 userB가 key 값을 localBkey = keyB로 셋팅했지만
- userA의 key값은 localAKey에 저장되어 있으므로 정상적으로 출력한다.
- userB도 자신의 key값을 localBKey에서 가져오므로 정상 동작한다.
package com.example.controller;
public class ThreadLocalCase {
private static ThreadLocal<String> keyDB = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Runnable localUserA = () -> {
try {
function_local("keyA");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable localUserB = () -> {
try {
function_local("keyB");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
System.out.println("ThreadLocal Start");
Thread threadA_Local = new Thread(localUserA);
threadA_Local.setName("threadA");
Thread threadB_Local = new Thread(localUserB);
threadB_Local.setName("threadB");
threadA_Local.start();
Thread.sleep(100);
threadB_Local.start();
Thread.sleep(3000);
System.out.println("ThreadLocal End");
}
public static void function_local(String keyName) throws InterruptedException {
keyDB.set(keyName);
Thread.sleep(1000);
System.out.println(keyName +" 의 key = " + keyDB.get());
keyDB.remove();
}
}