์๋น์ค๊ฐ ํฅํํ์ฌ ์ฌ์ฉ์๊ฐ ๋ง์์ง๋ ๊ฒฝ์ฐ ๋์ผํ ์ธ์คํด์ค์ ๋์ผํ ํ๋์ ์๋ก ๋ค๋ฅธ ์ฐ๋ ๋๊ฐ ๋์์ ์ ๊ทผํ๋ ๋น๋๊ฐ ๋์์ง ์ ์๋ค. ์ด๋ ๊ฒ ๋์์ ์ ๊ทผ๋์ ๋ ๋ฐ์ํ๋ ์ด์๋ฅผ ๋์์ฑ ์ด์๋ผ ํ๋ค.
๋์์ฑ ์ด์๊ฐ ๋ฐ์ํ๋ ์๋๋ฆฌ์ค๋ฅผ ๊ฐ๋จํ๊ฒ ๊ทธ๋ ค๋ณด์.
Spring์์ ๋์์ฑ ์ด์๋ฅผ ๋ ์ ๊ฒฝ์จ์ค์ผ ํ ์ด์ ๊ฐ ์๋ค.
Spring์ Bean์ ๋ฑ๋กํ๊ณ ์ฃผ์
๋ฐ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋งค์ฐ ์ผ๋ฐ์ ์ธ๋ฐ ๋ณดํต์ ๊ฒฝ์ฐ Bean์ Singleton์ผ๋ก ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ๋์ผํ ์ธ์คํด์ค์ ์ ๊ทผ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์ ๋ฌ๋ ํ๋ผ๋ฏธํฐ๋ฅผ store
ํ๋์ ์ ์ฅํ๊ณ ์ ์ฅ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์ด๋ค.
์ ์ฅํ๋๋ฐ 1์ด๊ฐ ์์๋๋ค๊ณ ๊ฐ์ ํ๊ณ , 1์ด ์ฌ์ด์ ๋ค๋ฅธ ์ฐ๋ ๋๊ฐ ๋์ผํ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ ํด์ ๋์์ฑ ์ด์๋ฅผ ํ์ธํ๋ค.
@Slf4j
public class FieldService {
private String store;
public String logic(String name) {
log.info("์ ์ฅ name={} -> store={}", name, store);
this.store = name;
sleep(1000); // ์ ์ฅํ๋๋ฐ 1์ด๊ฐ ๊ฒฝ๊ณผํ๋ค๊ณ ๊ฐ์ (1์ด๋ฅผ sleep ํ๋ ๋์ ๋ค๋ฅธ ์ฐ๋ ๋๋ฅผ ๋ผ์๋ฃ์ด์ ๋์์ฑ ์ด์๊ฐ ๋ฐ์ํ๋์ง ํ์ธํ๊ธฐ ์ํจ)
log.info("์กฐํ store={}", store);
return store;
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Slf4j
public class FieldServiceTest {
private FieldService fieldService = new FieldService();
@Test
void field() {
log.info("start");
Runnable userA = () -> fieldService.logic("userA");
Runnable userB = () -> fieldService.logic("userB");
Thread threadA = new Thread(userA);
threadA.setName("Thread-A");
Thread threadB = new Thread(userB);
threadB.setName("Thread-B");
threadA.start();
sleep(100); // 1000ms๊ฐ ๋๊ธฐ ์ ์ B์ฐ๋ ๋๋ฅผ ์์์์ผ์ ๊ฒฝํฉ์ํ๋ฅผ ๋ง๋ ๋ค.
threadB.start();
sleep(3000);
log.info("exit");
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
์กฐํ๊ฒฐ๊ณผ ๋ ์ฐ๋ ๋ ๋ชจ๋ userB
๋ฅผ ๋ฆฌํดํ๊ณ ์๋ค.
A์ฐ๋ ๋๊ฐ ๋ณ๊ฒฝํ ํ๋๋ฅผ B์ฐ๋ ๋๊ฐ ๋ฎ์ด์ฐ๊ณ ๋ ์ฐ๋ ๋ ๋ชจ๋ ๋ฎ์ด์ด ๊ฒฐ๊ณผ๋ฅผ ์ฝ์๊ธฐ ๋๋ฌธ์ด๋ค.
ThreadLocal์ ์ฐ๋ ๋๋ง๋ค ๊ณ ์ ํ Map ํํ ์ ์ฅ์๋ก ์๊ฐํ๋ฉด ๋๋ค.
์ธ์คํด์ค์ ํ๋๊ฐ ThreadLocal ํ์
์ด๋ผ๋ฉด ์ฐ๋ ๋๋ง๋ค ๋
๋ฆฝ์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๊ด๋ฆฌ๋๋ ๊ฒ์ด๋ค.
@Slf4j
public class ThreadLocalFieldService {
private ThreadLocal<String> nameStore = new ThreadLocal<>();
public String logic(String name) {
log.info("์ ์ฅ name={} -> nameStore={}", name, nameStore.get());
nameStore.set(name);
sleep(1000);
log.info("์กฐํ nameStroe={}", nameStore.get());
return nameStore.get();
}
}
์ฌ์ฉ๋ฒ์ ์๊ฐ๋ณด๋ค ๊ฐ๋จํ๋ค.
๋์์ฑ ์ด์๊ฐ ์ฐ๋ ค๋๋ ํ๋๋ฅผ ThreadLocal
๋ก ์ ์ธํ๊ณ get
, set
๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ ์ ์ฅํ๋ค.
ThreadLocal
์ ์ ์ฉํ ํ
์คํธ ๊ฒฐ๊ดด์ด๋ค.
ThreadLocal
์ ์ฌ์ฉํ์ง ์์์ ๊ฒฝ์ฐ ์ฐ๋ ๋B๊ฐ ํ๋์ ์ ๊ทผํ์ ๋ ์ด๋ฏธ ์ฐ๋ ๋A์์ ์ธํ
ํ ๊ฐ์ด ์กด์ฌํ์๋๋ฐ ThreadLocal
์ ์ ์ฉํ๋ ์ฐ๋ ๋A๊ฐ ํ๋์ ๊ฐ์ ์ธํ
ํ ํ ์ฐ๋ ๋B๊ฐ ํ๋์ ์ ๊ทผํ์ ๋ null
์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์๋ก ๋ค๋ฅธ ์ ์ฅ์๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฒ์ด๋ค.
์กฐํ๊ฒฐ๊ณผ ๋ํ ์๋ํ ๋๋ก ๊ฐ ์ฐ๋ ๋์์ ์ ์ฅํ ๊ฒฐ๊ณผ๊ฐ ์กฐํ๋์๋ค.
ThreadLocal
ํ์
์ผ๋ก ์ ์ธํ ํ๋๋ฅผ ์ ๊ฑฐํ ๋ ๋ฐ๋์ remove
๋ฅผ ํด์ค์ผ ํ๋ค.
ํ๋๋ฅผ ์ ๊ฑฐํ ๋ ํด๋น ํ๋์ null์ ๋ฃ์ด์ฃผ๋ ๋ฐฉ์์ด ์๋๊ณ ThreadLocal
์ remove
๋ฉ์๋๋ก ํ๋๋ฅผ ์ ๊ฑฐํด์ค์ผ ํ๋ค.
ํด๋ผ์ด์ธํธ๋ ์ฐ๋ ๋ ํ์์ ์ฐ๋ ๋๋ฅผ ํ ๋น๋ฐ์ ์ฌ์ฉํ๋ค.
์ค์ํ ๊ฒ์ ์์
์ด ์๋ฃ๋๋ฉด ์ฐ๋ ๋๋ฅผ ๋ค์ ์ฐ๋ ๋ ํ์ ๋ฐ๋ฉํ๋ค๋ ๊ฒ์ด๋ค. ํด๋น ์ฐ๋ ๋๋ฃ ์ ๊ฑฐํ๊ณ ์ฌ์ฉํ ๋ ๋ค์ ์์ฑํ๋ ๋ฐฉ์์ด ์๋๋ค. (์ฐ๋ ๋ ์์ฑ ๋น์ฉ์ด ๋น์ธ๊ธฐ ๋๋ฌธ...)
๋์ผํ ์ฐ๋ ๋๊ฐ ๋ค์ ์ฌ์ฉ๋๋ค๋ ๊ฒ์ธ๋ฐ ์ด์ ์ ์ฌ์ฉํ ์ฐ๋ ๋์ ThreadLocal
์ ๋ฐ์ดํฐ๊ฐ ๋จ์์๋ค๋ฉด..?
์กฐํ์ ์๋ํ ๊ฒฐ๊ณผ๊ฐ ์๋ ์ด์ ํด๋ผ์ด์ธํธ์ ๋ฐ์ดํฐ๊ฐ ์กฐํ๋ ์ ์๋ค.
์์ฒญ์ ๋ํ ์์
์ด ๋๋๋ฉด ํํฐ๋ ์ธํฐ์
ํฐ๋ฅผ ํตํ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ด๋ ๋ฐ๋์ ThreadLocal
์ remove
๋ฅผ ํตํด ThreadLocal
์ ์ฅ์๋ฅผ ํด์ ํด์ค์ผ ํ๋ค.
ThreadLocal<Item> item = new ThreadLocal<>();
.... ์์
....
item.remove();
์ธํ๋ฐ - ์คํ๋ง ํต์ฌ ์๋ฆฌ ๊ณ ๊ธํธ (๊น์ํ ๋)