[F-lab 모각코 챌린지 41일차] TIL

JeongheeKim·2023년 7월 11일

TIL

목록 보기
41/66

학습계획


  • volatile
  • ThreadLocal

Today I Learned


volatile

자바의 예약어로 변수 선언시에만 사용 가능하다.

예제)

VolatileSample클래스에 변수instanceVal의 값을 변경하는 thread job을 생성한다.

public class VolatileSample extends Thread {
    private double instanceVal = 0;

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

    public void run() {
        while (instanceVal == 0) {
            System.out.println(instanceVal);
        }
    }
}
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 end!!");
        sample.setDouble(-1);
        System.out.println(" set value is completed!!");
    }
}

예상은 instanceVal 값이 -1로 변경될거라 예상했지만 0.0이 계속 출력되었다.

예시처럼 각 쓰레드에서 수행되는 변수의 값을 반복적으로 참조할때는 메인 메모리에 저장되는것이 아닌 CPU 캐시에 저장된 값을 바라보게 된것이다. 쓰레드에 선언된 인스턴스 변수를 선언할 일이 있으면, volatile 키워드를 붙여 변수의 값을 메인메모리에 저장할 수 있다.

private volatile double instanceVal = 0;

volatile 키워드는 멀티 스레드 환경에서 하나의 thread만 read, write작업을 수행하고 나머지 스레드 에서는 read을 수행할 경우 가장 최신의 값을 보장할 수 있도록 도와준다.

단, 메인 메모리에 직접 값을 읽고 쓰는것이 CPU 캐시 에서 쓰는것보다 비용이 크므로 반복적인 값을 읽으며 값의 일치를 보장할때만 사용해야한다.

Thread Local

각 쓰레드별로 공유하지 않을 변수에 대해 ThreadLocal 클래스를 통해 선언할 수 있다.

  • ThreadLocal에 저장된 값은 해당 쓰레드에서 고유하게 사용할 수 있다.
  • ThreadLocal 클래스 변수는 private static final로 선언한다.
  • ThreadLocal 사용 후에는 remove()를 호출하는 습관을 가져야 한다.
    • 간단한 자바 프로그램에서는 쓰레드를 한번 생성되고 쓰레드가 종료된다. 하지만 웹 어플리케이션의 경우 쓰레드가 시작 된 후 종료되는것이 아닌 쓰레드 풀을 사용하여 보관하기 때문 그러므로 사용한 쓰레드에 대해 remove()를 호출하는것을 권장한다.
package ch00;

import java.util.Random;

public class ThreadLocalTest {
    private final static ThreadLocal<Integer> local = new ThreadLocal<>();//쓰레드 로컬 객체 생성
    private static Random random;
    static {
        random = new Random();
    }

    public static Integer generateNumber() {
        int value = random.nextInt();
        local.set(value);
        return value;
    }
    public static Integer get() {
        return local.get();
    }
    public static void remove() {
        local.remove();
    }
}
public class OtherLogic {
    public void printMyNumber() {
        System.out.println(Thread.currentThread().getName() + " Other Logic value = " + ThreadLocalTest.get
                ());
    }
}
public class LocalUserThread extends Thread {

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

        OtherLogic logic = new OtherLogic();
        logic.printMyNumber();
        ThreadLocalTest.remove();
    }
}
public class Main {
    public static void main(String[] args) {
        LocalUserThread thread = new LocalUserThread();
        thread.start();

        LocalUserThread thread1 = new LocalUserThread();
        thread1.start();

        LocalUserThread thread2 = new LocalUserThread();
        thread2.start();
    }
}

//Thread-0 LocalUserThread value = -1663812860
//Thread-2 LocalUserThread value = 285197657
//Thread-1 LocalUserThread value = -1685341318
//Thread-1 Other Logic value = -1685341318
//Thread-2 Other Logic value = 285197657
//Thread-0 Other Logic value = -1663812860

예제를 보면 ThreadLocal 변수가 각 각의 스레드에서 스레드 마다 고유값을 유지하는것을 확인 할 수 있다.

ThreadLocal은 Thread정보를 키값으로 저장하는 Map(ThreadLocalMap)의 구조를 사용하여 Thread별 ThreadLocal 변수가 Thread별 고유한 값을 유지할 수 있다. ThreadLocal클래스의 get()를 확인하면 현재 진행 중인 thread를 키값으로 getMap()을 통해 ThreadLocalMap을 불러온다. ThreadLocalMap에는 ThreadLocal객체와 ThreadLocal의 T타입이 저장된다.

Untitled

ThreadLocalMap은 Thread 클래스에서 찾을 수 있다. Thread클래스에서 ThreadLocal클래스의 ThreadLocalMap을 참조하고 있다.


(보충) Custom Exception 클래스 만들면 versionUID 오버라이딩 하는 이유는 ?
custom exception을 구현할때 상속받는 Runtime Exception의 부모 Throwable은 Serializable 인터페이스를 구현하고 있다. custome exception의 상태값이 변경되면 serialversionUID값이 변경되는데 자식 클래스에서 명시적인 serialversionUID값을 선언하지 않으면 부모의 serialversionUID값을 참조하게 되어 역직렬화 시 제대로된 객체 버전 확인이 어려울 수 있다.


알고리즘
문자열을 아스키 코드로 변환 :ord()
아스키 코드를 문자열로 변환 : chr()
단, 변환 시 문제에서 알파벳 범위로 지정했기에 아스키코드로 변환 후 n만큼 커질 경우 알파벳 범위를 벗어나는 경우가 있다.
알파벳은 총 26글자 이므로 원래 글자에서 알파벳에서 얼마나 떨어져 있는지 계산한 뒤 숫자를 더하고, 26으로 나눈 나머지를 취한다.

0개의 댓글