๐Ÿ”ฅ TIL - Day 77 ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ - ThreadLocal

Kim Dae Hyunยท2022๋…„ 1์›” 1์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
88/93

์„œ๋น„์Šค๊ฐ€ ํฅํ–‰ํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ์•„์ง€๋Š” ๊ฒฝ์šฐ ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค์˜ ๋™์ผํ•œ ํ•„๋“œ์— ์„œ๋กœ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋Š” ๋นˆ๋„๊ฐ€ ๋†’์•„์งˆ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋™์‹œ์— ์ ‘๊ทผ๋์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด์Šˆ๋ฅผ ๋™์‹œ์„ฑ ์ด์Šˆ๋ผ ํ•œ๋‹ค.

๋™์‹œ์„ฑ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ทธ๋ ค๋ณด์ž.

  • ์“ฐ๋ ˆ๋“œ A๊ฐ€ X์ธ์Šคํ„ด์Šค์œผ ์–ด๋–ค ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•ด์„œ X์ธ์Šคํ„ด์Šค์˜ ํ•„๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์ˆ˜์ • ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ์“ฐ๋ ˆ๋“œ A๊ฐ€ ํ•„๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋™์‹œ์— ์“ฐ๋ ˆ๋“œ B๊ฐ€ ๋™์ผํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • ์“ฐ๋ ˆ๋“œ A๋Š” X์ธ์Šคํ„ด์Šค์˜ ํ•„๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์“ฐ๋ ˆ๋“œ B๋„ ๋™์ผํ•œ ํ•„๋“œ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์“ฐ๋ ˆ๋“œ A์™€ B ๋ชจ๋‘ B๊ฐ€ ์ˆ˜์ •ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋œ๋‹ค.
  • A์˜ ์ˆ˜์ •๊ฒฐ๊ณผ๋ฅผ B๊ฐ€ ๋ฎ์–ด์“ด ๊ฒƒ.

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 ์„ ์ด์šฉํ•œ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ

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 ์‚ฌ์šฉ์‹œ ์ฃผ์˜์‚ฌํ•ญ

ThreadLocal ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•œ ํ•„๋“œ๋ฅผ ์ œ๊ฑฐํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ remove๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

ํ•„๋“œ๋ฅผ ์ œ๊ฑฐํ•  ๋•Œ ํ•ด๋‹น ํ•„๋“œ์— null์„ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ณ  ThreadLocal์˜ remove ๋ฉ”์„œ๋“œ๋กœ ํ•„๋“œ๋ฅผ ์ œ๊ฑฐํ•ด์ค˜์•ผ ํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ๋Š” ์“ฐ๋ ˆ๋“œ ํ’€์—์„œ ์“ฐ๋ ˆ๋“œ๋ฅผ ํ• ๋‹น๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค.
์ค‘์š”ํ•œ ๊ฒƒ์€ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ์“ฐ๋ ˆ๋“œ๋ฅผ ๋‹ค์‹œ ์“ฐ๋ ˆ๋“œ ํ’€์— ๋ฐ˜๋‚ฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํ•ด๋‹น ์“ฐ๋ ˆ๋“œ๋“ฃ ์ œ๊ฑฐํ•˜๊ณ  ์‚ฌ์šฉํ•  ๋•Œ ๋‹ค์‹œ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๋‹ค. (์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ ๋น„์šฉ์ด ๋น„์‹ธ๊ธฐ ๋•Œ๋ฌธ...)

๋™์ผํ•œ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋‹ค์‹œ ์‚ฌ์šฉ๋˜๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ ์ด์ „์— ์‚ฌ์šฉํ•œ ์“ฐ๋ ˆ๋“œ์˜ ThreadLocal์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ๋‹ค๋ฉด..?

์กฐํšŒ์‹œ ์˜๋„ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹Œ ์ด์ „ ํด๋ผ์ด์–ธํŠธ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กฐํšŒ๋  ์ˆ˜ ์žˆ๋‹ค.

์š”์ฒญ์— ๋Œ€ํ•œ ์ž‘์—…์ด ๋๋‚˜๋ฉด ํ•„ํ„ฐ๋‚˜ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ํ†ตํ•˜๋“  ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด๋“  ๋ฐ˜๋“œ์‹œ ThreadLocal์˜ remove๋ฅผ ํ†ตํ•ด ThreadLocal์ €์žฅ์†Œ๋ฅผ ํ•ด์ œํ•ด์ค˜์•ผ ํ•œ๋‹ค.

ThreadLocal<Item> item = new ThreadLocal<>();

.... ์ž‘์—… ....

item.remove();


๐Ÿ“Œ ์ฐธ๊ณ 

์ธํ”„๋Ÿฐ - ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ ๊ณ ๊ธ‰ํŽธ (๊น€์˜ํ•œ ๋‹˜)

profile
์ข€ ๋” ์ฒœ์ฒœํžˆ ๊นŒ๋จน๊ธฐ ์œ„ํ•ด ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๐Ÿง

0๊ฐœ์˜ ๋Œ“๊ธ€