[Java] ThreadLocal

Hyunho·2021년 11월 17일
0

ThreadLocal

Spring에서 Bean을 Singleton으로 관리하기때문에 여러 thread가 bean객체에 접근을 하게 되면 동시성 문제가 발생하며, 이 문제점을 해결하기 위해 ThreadLocal을 사용 한다.

ThreadLocal은 해당 thread만 접근할 수 있는 저장소를 말한다. ThreadLocal을 사용하면 각 쓰레드마다 별도의 내부 저장소를 제공하여 같은 인스턴스의 ThreadLocal에 접근해도 문제가 발생하지 않는다.

ThreadLocal 사용법

private ThreadLocal<Type> threadLocal = new ThreadLocal<>();

값 저장 - threadLocal.set(value);
값 조회 - threadLocal.get();
값 제거 - threadLocal.remove();

Thread가 ThreadLocal을 모두 사용하고 나면 threadLocal.remove();를호출해서 ThreadLocal에 저장된 값을 제거해주어야한다

예제

  • ThreadLocal적용 X
@Slf4j
public class FieldService {
    private  String nameStore;

    public String logic(String name){
        log.info("저장 name = {} -> nameStore={}", name, nameStore);
        nameStore = name;
        sleep(1000);
        log.info("조회 nameStore={}", nameStore);
        return nameStore;
    }
    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("main 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);//동시성 문제 발생
        threadB.start();
        //countdown letgi
        sleep(3000);//메인 쓰레드 종료 대기
        log.info("main exit");
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

위 코드는 하나의 필드인FieldServicenameStore 에 두개의 쓰레드가 접근하여 ThreadAnameStore 값을 출력하기전에 ThreadBnameStore 값을 변경하여 위와같은 문제가 발생

  • 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("조회 nameStore={}", nameStore.get());
        return nameStore.get();
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
@Slf4j
public class ThreadFieldServiceTest {

    private ThreadLocalFieldService service = new ThreadLocalFieldService();

    @Test
    void field() {
        log.info("main start");
        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();
        sleep(100);
        threadB.start();
        //countdown letgi
        sleep(3000);//메인 쓰레드 종료 대기
        log.info("main exit");
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Reference

스프링 핵심 원리-김영한

profile
hyunho

0개의 댓글