Functional Programming in Java 8 책의 6장에 Lambda를 이용하여 Lazy Initialization을 하는 방법이 나온다.이 예제는 Heavy 객체의 생성을 Supplier 인터페이스를 이용해서 사용할 시점까지 지연시키는 예제다.하지만 이 예제는 Heavy 클래스가 아닌 또 다른 heavy한 객체를 생성해야 할 때 매번 해당 클래스에 대한 HeavyInstanceHolder 클래스를 만들어 주어햐 하는 단점이 있다.
Jake라는 분이 이를 해결한 방법 Blog로 정리해 놓았다. 요점은 Heavy 클래스를 Lazy로 생성하는 부분을 T 형 타입으로 받아서 처리하면 된다.
그런데 synchronized 키워드를 붙여 thread-safe 하게 처리한 부분이 좀 이상해서 github을 따라 갔더니 코드가 조금 변경되어 있다.
해당 github 소스:
- test용 main 클래스
- Lazy Initialization 구현 클래스
하지만 여전히 문제가 있다. main 클래스에서 Heavy 객체를 생성하는 게 잘 표현되어 있지 않다!! Venkat(책 저자)의 의도가 드러나게 다시 바꿔보자.
먼저 초기화에 시간이 오래 걸리는 Heavy 클래스가 있다.
public class Heavy {
private int n = 0;
public Heavy() {
System.out.println("Heavy Initiation started from " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Heavy Initiation ended from " + Thread.currentThread().getName());
}
public int next() {
return ++n;
}
}
이 놈을 여러번 생성하는 테스트 클래스다. 2개의 쓰레드에서 Heavy 객체를 동시에 생성해 보자. 그리고 생성 후 Heavy 객체의 메소드를 호출해서 2번째 쓰레드가 같은 객체를 또 생성하지는 않는지, 지연 생성이 완료될 때까지 기다리고 있는 지도 확인 해 보자.
import java.io.IOException;
import java.util.function.Supplier;
public class Main2 {
public static void main(String[] args) throws IOException {
System.out.println("Main start");
// type is defined when calling LazilyInstantiate.using
LazilyInstantiate<Heavy> heavy = LazilyInstantiate.using(heavyTaskSupplier());
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread1 run");
System.out.println(heavy.get().next());
}
}, "Thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread2 run");
System.out.println(heavy.get().next());
}
}, "Thread1").start();
System.out.println("Main end");
}
private static Supplier<Heavy> heavyTaskSupplier() {
return new Supplier<Heavy>() {
@Override
public Heavy get() {
return new Heavy();
}
};
}
}
LazilyInstantiate 클래스다. Supplier를 이용해서 Heavy 생성 시점을 지연시키고, 한 번 생성된 객체는 재활용한다.
import java.util.function.Supplier;
public class LazilyInstantiate<T> implements Supplier<T> {
private final Supplier<T> supplier;
private Supplier<T> current;
public static <T> LazilyInstantiate<T> using(Supplier<T> supplier) {
return new LazilyInstantiate<>(supplier);
}
private LazilyInstantiate(Supplier<T> supplier) {
this.supplier = supplier;
this.current = () -> swapper();
}
private synchronized T swapper() {
if (!Factory.class.isInstance(current)) {
current = new Factory(supplier.get());
}
return current.get();
}
@Override
public T get() {
return current.get();
}
class Factory implements Supplier<T> {
private T instance;
public Factory(T t) {
this.instance = t;
}
@Override
public T get() {
return instance;
}
}
}
import java.util.function.Supplier;
public class LazilyInstantiate<T> implements Supplier<T> {
private final Supplier<T> supplier;
private Supplier<T> current;
public static <T> LazilyInstantiate<T> using(Supplier<T> supplier) {
return new LazilyInstantiate<>(supplier);
}
private LazilyInstantiate(Supplier<T> supplier) {
this.supplier = supplier;
this.current = () -> swapper();
}
private synchronized T swapper() {
if (!Factory.class.isInstance(current)) {
current = new Factory(supplier.get());
}
return current.get();
}
@Override
public T get() {
return current.get();
}
class Factory implements Supplier<T> {
private T instance;
public Factory(T t) {
this.instance = t;
}
@Override
public T get() {
return instance;
}
}
}
Main을 실행한 결과
Main start
Thread1 run
Main end
Thread2 run
Heavy Initiation started from Thread1
Heavy Initiation ended from Thread1
1
2
이제 Main 함수에서 Heavy 객체 외에 다른 객체(Heavy2)의 생성을 지연시키고 싶다면, 이렇게만 하면 된다.
LazilyInstantiate<Heavy2> heavy = LazilyInstantiate.using(heavyTaskSupplier2());
private static Supplier<Heavy2> heavyTaskSupplier() {
return new Supplier<Heavy2>() {
@Override
public Heavy get() {
return new Heavy2();
}
};
}
'Tips > Java' 카테고리의 다른 글
Files.find를 이용하여 디렉토리 내 모든 파일 리스트 출력하기 (0) | 2016.09.01 |
---|---|
JUnit Test 클래스를 main함수로 실행하기 (0) | 2015.01.05 |
[JAVA] 문자열 내에서 숫자만 분리하기 (0) | 2014.05.09 |
[JAVA] Regular Expression (정규 표현식) 예제 (0) | 2013.03.16 |
[JAVA] 정규표현식 - URL (0) | 2013.03.15 |