자바의 예약어로 변수 선언시에만 사용 가능하다.
예제)
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 캐시 에서 쓰는것보다 비용이 크므로 반복적인 값을 읽으며 값의 일치를 보장할때만 사용해야한다.
각 쓰레드별로 공유하지 않을 변수에 대해 ThreadLocal 클래스를 통해 선언할 수 있다.
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타입이 저장된다.

ThreadLocalMap은 Thread 클래스에서 찾을 수 있다. Thread클래스에서 ThreadLocal클래스의 ThreadLocalMap을 참조하고 있다.
(보충) Custom Exception 클래스 만들면 versionUID 오버라이딩 하는 이유는 ?
custom exception을 구현할때 상속받는 Runtime Exception의 부모 Throwable은 Serializable 인터페이스를 구현하고 있다. custome exception의 상태값이 변경되면 serialversionUID값이 변경되는데 자식 클래스에서 명시적인 serialversionUID값을 선언하지 않으면 부모의 serialversionUID값을 참조하게 되어 역직렬화 시 제대로된 객체 버전 확인이 어려울 수 있다.
알고리즘
문자열을 아스키 코드로 변환 :ord()
아스키 코드를 문자열로 변환 : chr()
단, 변환 시 문제에서 알파벳 범위로 지정했기에 아스키코드로 변환 후 n만큼 커질 경우 알파벳 범위를 벗어나는 경우가 있다.
알파벳은 총 26글자 이므로 원래 글자에서 알파벳에서 얼마나 떨어져 있는지 계산한 뒤 숫자를 더하고, 26으로 나눈 나머지를 취한다.