Java transient volatile Keyword
Querydsl에서 Expression이 적용되는 방식을 찾아보다가 transient, volatile 키워드를 발견하게 되었는데요.
해당 키워드는 Expression 외에도 Hashtable, ConcurrentHashMap, PredicateOperation, PredicateTemplate 등 여러 곳에서 사용되고 있었으며, 지난번에도 한번 스쳐간 기억이 있어 이번 기회를 통해 정리해 보게 되었습니다.
우선 'transient'와 'volatile' keyword는 전혀 다른 역할을 하는데요.
transient는 객체의 직렬화 과정에서 사용되는 키워드이며, volatile은 멀티 스레드 환경에서 여러 스레드에 의해 동시에 접근될 수 있는 변수의 가시성과 관련된 키워드입니다.
두 가지 모두 개발하며 직접 사용할 일은 거의 없다고 하지만, 이러한 개념이 있다는 것 정도는 알아두면 좋을 것 같습니다.
transient
@ToString
@Getter
@AllArgsConstructor
public class Product implements Serializable {
private String name;
private Long price;
private transient Long stock;
}
(Product class)
//serialize
try{
FileOutputStream fos = new FileOutputStream("product.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//object
Product targetProduct = new Product("productName", 1000L, 50L);
System.out.println("before serialize: " + targetProduct);
//Serialize the object
oos.writeObject(targetProduct);
//close
fos.close();
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
//deserialize
try {
FileInputStream fis = new FileInputStream("product.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
//read
Product targetProduct = (Product) ois.readObject();
System.out.println("after deserialize: " + targetProduct);
//close
fis.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
// before serialize: Product(name=productName, price=1000, stock=50)
// after deserialize: Product(name=productName, price=1000, stock=null)
(예시 코드, Object를 File로 serialize 할 때 자바 표준 규칙으로 '.ser' 확장자가 사용됩니다.)
위 코드는 Product 객체의 stock 멤버 변수에 'transient' 키워드를 적용한 예시입니다.
이처럼 transient는 객체 직렬화 프로세스에서 특정 필드 멤버에 대한 키워드를 적용하여 직렬화에서 제외시킬 수 있는데요.
이는 해당 멤버 변수가 개체의 영구 상태에 속하지 않는다는 것을 JVM(Java Virtual Machine)에 나타낸다고도 할 수 있으며, 메모리 안에서만 사용되어야 하는 변수라고도 볼 수 있습니다.
(transient 키워드는 static, final 키워드와 함께 사용할 수 없다는 특징이 있습니다.)
***
자바 시스템 내부에서 사용되는 객체(Object) 또는 데이터(Data)를 외부 자바 시스템에서 사용할 수 있도록 바이트(byte) 형태의 데이터로 변환하는 기술을 '직렬화(Serialization)'라고 하며, 반대로 바이트로 변환된 데이터를 원래 객체(Object) 또는 데이터(Data)로 변환하는 기술을 '역직렬화(Deserialization)'라고 합니다.
volatile
'volatile' 키워드가 선언된 변수는 해당 변수 값을 cpu의 cache가 아닌 main memory에 저장하게 되며, 이러한 방식을 통해 멀티 스레드 환경에서의 가시성을 보장하는데요.
개념만 가지고는 이해가 어려울 수 있기 때문에 아래 예시를 통해 자세하게 살펴보겠습니다.
(volatile 키워드는 메서드나 클래스에는 사용할 수 없으며, 변수에만 사용 가능합니다. 또한 static, fianl 키워드와 함께 사용할 수 있다는 특징이 있습니다.)
서로 다른 cpu의 각 thread에서 공유된 변수(non-volatile)에 액세스 한다고 가정합니다.
각 스레드는 값을 저장할 때 cache에 먼저 저장한 뒤 main memory에 기록하는 과정을 거치는데요.
이때 non-volatile 변수의 경우 cache에 저장된 값이 main memory에 즉시 기록되지 않기 때문에 기록되는 시기가 보장되지 않는다는 문제점이 있습니다.
따라서 첫 번째 스레드에서 count 변수에 3이라는 값을 저장하고, 두 번째 스레드에서 count 변수 값을 가져오려는 상황에서, 만약 첫 번째 스레드에서 저장된 count 값이 main memory(count = 0)에 저장되지 않은 상태라면 두 번째 스레드에서는 업데이트되지 않은 값(count = 0)을 읽어올 수 있습니다.
같은 상황에서 만약 변수에 volatile 키워드를 선언한 경우, volatile가 선언된 변수는 cache를 거치지 않고 바로 main memory에 저장되기 때문에 이러한 다중 스레드 환경에서 최신 업데이트 값을 보장하게 되는 것입니다.
하지만 해당 키워드를 선언한다고 해서 무조건 멀티 스레드 환경에서의 가시성이 보장되는 것은 아닌데요.
multi thread 환경에서 하나의 스레드만 read, write 작업을 수행하고 나머지 스레드들은 read만 하는 경우 가시성이 보장되며, 만약 여러 스레드에서 write 작업을 해야 하는 경우 synchronized 키워드를 통해 변수에 대한 원자성(atomic)을 추가로 보장해야 합니다.
***
cache를 사용하는 것보다 main memory를 사용했을 때의 비용이 더 크기 때문에 꼭 필요한(멀티 스레드 환경에서 변수 값이 보장되어야 하는) 경우에만 volatile 키워드를 사용하는 것이 좋습니다.
< Java 직렬화 역직렬화 관련 자료 >
2021.12.13 - [Programming/Java] - Java 직렬화, 역직렬화 방법 Serializable interface
< 참고 자료 >
https://www.baeldung.com/java-volatile-variables-thread-safety
https://nesoy.github.io/articles/2018-06/Java-volatile
https://medium.com/an-idea/difference-between-transient-vs-volatile-variable-in-java-de110176e916
'Programming > Java' 카테고리의 다른 글
SOLID 객체 지향 프로그래밍의 5가지 원칙 (0) | 2023.08.14 |
---|---|
java @Builder 기능 더 활용하기(toBuilder, @Singular 등) (0) | 2023.08.05 |
java 임시 비밀번호 생성(SecureRandom 사용하는 이유) (0) | 2023.07.30 |
Java Map 반복시키는 가장 효율적인 방법 (0) | 2023.07.26 |
java stream BigDecimal add 합계 구하는 방법 (0) | 2023.07.19 |