ThreadLocal과 volatile

Yong-hyun Kim·2021년 11월 26일
0

ThreadLocal이란

  • 각 Thread에서만 따로 사용해야하는 변수가 필요할 때 사용한다.
  • 스레드 별로 서로 다른 값을 처리해야하는 필요가 있을때 사용
class ThreadLocalSample{
    private final static ThreadLocal<Integer> local = new ThreadLocal<>();
    private static Random random;

    static{
        random = new Random();
    }

    public static Integer generateNumber(){
        int value = random.nextInt(45);
        local.set(value);
        return value;
    }

    public static Integer get(){
        return local.get();
    }
}

위의 소스에서 ThreadLocal객체를 각 스레드에서 별도로 사용하기 위해 선언된 부분이다. 사용되는 부분의 소스는

public class LocalUserThread  extends Thread{
    public void run(){
        int value = ThreadLocalSample.generateNumber();
        System.out.println(this.getName() + " LocalUserThread value = " + value);

        OtherLogic OtherLogic = new OtherLogic();
        OtherLogic.printMyName();
        
    }
    
}
class OtherLogic{
    public void printMyName(){
        System.out.println(Thread.currentThread().getName());
        System.out.println(ThreadLocalSample.get());
    }
}


    public static void main(String[] args) {
        LocalUserThread t1 = new LocalUserThread();
        LocalUserThread t2 = new LocalUserThread();
        LocalUserThread t3 = new LocalUserThread();

        t1.start();
        t2.start();
        t3.start();

    }

위의 소스에서 thread가 각각 생성되어 실행될때

아래과 같은 결과를 얻을 수 있다.

thread-0 LocalUserThread value = 12
Thread-1 LocalUserThread value = 20
Thread-2 LocalUserThread value = 32
Thread-0 LocalUserThread value = 12
Thread-2 LocalUserThread value = 32
Thread-1 LocalUserThread value = 20

결과적으로 각 스레드에서 사용하는 ThreadLocal객체에 담긴 변수값을 변경없이 그대로 사용할 수 있다는 장점이 있다.

Volatile


public class RunVolatile {
    public static void main(String[] args) {
        RunVolatile sample = new RunVolatile();
        sample.runVolatileSample();
    }

    public void runVolatileSample(){
        VolatileSample sample = new VolatileSample();
        sample.start();
        try{
            Thread.sleep(1000);
        }catch(Exception e){
            e.printStackTrace();
        }

        System.out.println("Sleep ended!!");
        sample.setDouble(-1);
        System.out.println("Set value is completed!!");
    }
}

class VolatileSample extends Thread{
    private double instanceVariable = 0;

    public void setDouble(double value){
        this.instanceVariable = value;
    }

    public void run(){
        while(instanceVariable == 0);
        System.out.println(instanceVariable);
    }
}

위의 소스를 실행하면 종료되지 않는다.
이 문제를 해결하기 좋은 키워드가 volatile이다.
이 문제에 원인은 CPU 캐시때문이다. 분명 스레드에 값을 변경했는데 프로그램이 종료되지 않는 현상이 존재한다.
이렇게 값을 반복적으로 참조할때는 메인메모리에 있는 값을 가져오는것이 아닌 CPU 캐시에 있는 값을 저장하고 참조한다.

이때 변수선언부분을
private volatile double instanceVariable = 0;
변경하게되면 정상적으로 종료되는 것을 볼 수 있다.

즉 volatile의 의미는 해당 변수를 메인메모리에 저장하겠다는 뜻이다.

그럼 항상 사용하면 좋은걸까?

먼저
syncronized 개념과 다른점은 몰까?

syncronized는 변수를 원자적 계산이 가능하게 하는거고
volatile은 메인메모리에서 값을 가져와 항상 최신의 값을 유지하도록 하는것이다.

그럼 volatile을 사용하면 항상 원자적인 계산이 가능할까?

  • 아니다 (변수가 다른스레드에 의해 변경된것은 알 수 있지만 계산에 대해서는 lock을 걸지 않는다.)
profile
나를 꺾어봐

0개의 댓글