マルチスレッド環境下でCache機能を作る時、同時更新を防ぐためにsynchronizedをsetterに付け、getterからsetterを呼び出してあげる。
パフォーマンスのためgetterにはsynchronizedをつけない。getter内でsetterを呼ぶかどうか判断するときも、別スレッドが同時に判断した可能性があるので、setter内で再度同じ判断をしなければならない。

import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;


public final class Cache {

    /** コンストラクタ */
    private Cache() {
    }

    /**
     * ユーザ一覧
     */
    private static List<User> allUsers = null;

    /**
     * ユーザ一覧の有効期限
     */
    private static Date expirationOfAllUsers = new Date();

    /**
     * @return ユーザ一覧
     */
    public static List<User> getAllUsers() {
        if (expired()) {
            reload();
        }
        return allUsers;
    }

    private static synchronized void reload() {
        // synchronizedメソッドの中で再度、本当に取得する必要があるか確認する。
        if (expired()) {
            // コストの高いユーザ一覧取得処理
            allUsers = highCostProc();
            // 有効期限を更新
            expirationOfAllUsers = DateUtils.addMinutes(new Date(), 60);
        }
    }
    
    private static boolean expired() {
        Date now = new Date();
        return now.after(expirationOfAllUsers);
    }
}