In my app I have a "Manager" class that has a reference to a DAO class which loads data from DB and populate a HashMap as a cache solution.
Here is a snippet
class UserManager {
private final UserDAO userDAO;
private final Map<Long, User> users;
private final StampedLock lock = new StampedLock();
public UserManager() {
loadUsersFromDB();
}
// 99% of the times this method is called
public getUserById(long id) {
long stamp = lock.readLock();
try {
return users.get(id);
} finally {
lock.unlock(stamp);
}
}
// This is done once when CTOR calls it and from time to time
// by explicitly calling it from outside.
public void loadUsersFromDB() {
Map<Long, User> loadedUsers = userDAO.loadUsers();
long stamp = lock.writeLock();
try {
this.users = loadedUsers;
} finally {
lock.unlock(stamp);
}
}
}
This code work in a multithreaded environment, and the concern here is the use of StampedLock in this situation is an OVERKILL since most of the time it does READ operations and once in a while a simple load from DB and assignment to a class member.
I'm thinking to remove the StampedLock and instead use a simple AtomicReference<Map<Long, User>>, this way most of the time it's going to be a simple get and once in a while a set.
What do you think??
Related
I have an application that uses Hibernate and it's running out of memory with a medium volume dataset (~3 million records). When analysing the memory dump using Eclipse's Memory Analyser I can see that StatefulPersistenceContext appears to be holding a copy of the record in memory in addition to the object itself, doubling the memory usage.
I'm able to reproduce this on a slightly smaller scale with a defined workflow, but am unable to simplify it to the level that I can put the full application here. The workflow is:
Insert ~400,000 records (Fruit) into the database from a file
Get all of the Fruits from the database and find if there are any complementary items to create ~150,000 Baskets (containing two Fruits)
Retrieve all of the data - Fruits & Baskets - and save to a file
It's running out of memory at the final stage, and the heap dump shows StatefulPersistenceContext has hundreds of thousands of Fruits in memory, in addition to the Fruits we retrieved to save to the file.
I've looked around online and the suggestion appears to be to use QueryHints.READ_ONLY on the query (I put it on the getAll), or to wrap it in a Transaction with the readOnly property set - but neither of these seem to have stopped the massive StatefulPersistenceContext.
Is there something else I should be looking at?
Examples of the classes / queries I'm using:
public interface ShoppingService {
public void createBaskets();
public void loadFromFile(ObjectInput input);
public void saveToFile(ObjectOutput output);
}
#Service
public class ShoppingServiceImpl implements ShoppingService {
#Autowired
private FruitDAO fDAO;
#Autowired
private BasketDAO bDAO;
#Override
public void createBaskets() {
bDAO.add(Basket.generate(fDAO.getAll()));
}
#Override
public void loadFromFile(ObjectInput input) {
SavedState state = ((SavedState) input.readObject());
fDAO.add(state.getFruits());
bDAO.add(state.getBaskets());
}
#Override
public void saveToFile(ObjectOutput output) {
output.writeObject(new SavedState(fDAO.getAll(), bDAO.getAll()));
}
public static void main(String[] args) throws Throwable {
ShoppingService service = null;
try (ObjectInput input = new ObjectInputStream(new FileInputStream("path\\to\\input\\file"))) {
service.loadFromFile(input);
}
service.createBaskets();
try (ObjectOutput output = new ObjectOutputStream(new FileOutputStream("path\\to\\output\\file"))) {
service.saveToFile(output);
}
}
}
#Entity
public class Fruit {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
// ~ 200 string fields
}
public interface FruitDAO {
public void add(Collection<Fruit> elements);
public List<Fruit> getAll();
}
#Repository
public class JPAFruitDAO implements FruitDAO {
#PersistenceContext
private EntityManager em;
#Override
#Transactional()
public void add(Collection<Fruit> elements) {
elements.forEach(em::persist);
}
#Override
public List<Fruit> getAll() {
return em.createQuery("FROM Fruit", Fruit.class).getResultList();
}
}
#Entity
public class Basket {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#OneToOne
#JoinColumn(name = "arow")
private Fruit aRow;
#OneToOne
#JoinColumn(name = "brow")
private Fruit bRow;
public static Collection<Basket> generate(List<Fruit> fruits) {
// Some complicated business logic that does things
return null;
}
}
public interface BasketDAO {
public void add(Collection<Basket> elements);
public List<Basket> getAll();
}
#Repository
public class JPABasketDAO implements BasketDAO {
#PersistenceContext
private EntityManager em;
#Override
#Transactional()
public void add(Collection<Basket> elements) {
elements.forEach(em::persist);
}
#Override
public List<Basket> getAll() {
return em.createQuery("FROM Basket", Basket.class).getResultList();
}
}
public class SavedState {
private Collection<Fruit> fruits;
private Collection<Basket> baskets;
}
Have a look at this answer here... How does Hibernate detect dirty state of an entity object?
Without access to the heap dump or your complete code, I would believe that you are seeing exactly what you are saying that you see. As long as hibernate believes that it is possible that the entities will change, it keeps a complete copy in memory so that it can compare the current state of the object to the state as it was originally loaded from the database. Then at the end of the transaction (the transactional block of code), it will automatically write the changes to the database. In order to do this, it needs to know what the state of the object used to be in order to avoid a large number of (potentially expensive) write operations.
I believe that setting the transaction-block so that it is read-only is a step on the right-track. Not completely sure, but I hope the information here helps you at least understand why you are seeing large memory consumption.
1: Fetching all Fruits at once from DB, or Persisting large set of bucket once will impact DB performance as well as application performance because of huge objects in Heap memory (young gen + Old gen based on Object survive in heap). Use batch process instead of processing all data once.
use spring batch or implement or a custom logic to process data in set of chunks.
2: The persistence context stores newly created and modified entities in memory. Hibernate sends these changes to the database when the transaction is synchronized. This generally happens at the end of a transaction. However, calling EntityManager.flush() also triggers a transaction synchronization.
Secondly, the persistence context serves as an entity cache, also referred to as the first level cache. To clear entities in the persistence context, we can call EntityManager.clear().
Can take ref for batch processing from here.
3.If you don't plan on modifying Fruit, you could just fetch entries in read-only mode: Hibernate will not retain the dehydrated state which it normally uses for the dirty checking mechanism. So, you get half the memory footprint.
Quick Solution: If you just execute this method one time for db create increase jvm -Xmx value.
Real Solution: When you try to persist everything it will keep all datas in memory until commit, and memory easily consume, so rather than this, try to save datas part part like this dump modes. For example:
EntityManager em = ...;
for (Fruid fruid : fruids) {
try {
em.getTransaction().begin();
em.persist(fruid);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
if (em.isOpen())
em.close();
}
}
I have a use-case where I have configured multiple cache managers with different properties, and different methods annotated with a separate cache name.
The cached methods are retrieving data asynchronously from a http client, and caching the response. In the said use-case, the data from both the cached method is merged before returning the result. At times, the result contains data only from one of the cached methods, and on refreshing the issue is resolved.
I am not able to understand in what instance the issue is raised?
#Configuraions
public class CacheConfig{
public static final String CACHE1 = "cache1";
public static final String CACHE2 = "cache2";
#Value("${cache.caffeineSpec:expireAfterWrite=43200s,maximumSize=1000,recordStats}")
private String cacheSpec1;
#Value("${cache.caffeineSpec: expireAfterWrite=3600s,maximumSize=2000,recordStats}")
private String cacheSpec2;
#Bean("cacheManager1")
#Primary
public CacheManager brokerDetailscacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE1);
cacheManager.setCaffeine(Caffeine.from(cacheSpec1));
return cacheManager;
}
#Bean("cacheManager2")
public CacheManager brokerTierCodeMapCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE2, BROKER_TIER_CACHE);
cacheManager.setCaffeine(Caffeine.from(cacheSpec2));
return cacheManager;
}
}
Models in use
public class Person {
private String firstname;
private String lastname;
private List<Address> adresses;
}
private class Address {
private String street;
private String City
private String zip;
}
private class PersonInfo {
private String firstname;
private String lastname;
private Address address;
}
The cached method classes are:
#Service
#RequiredArgsConstructor
public class PersonCache {
private final DataClient dataClient;
#Cacheable(cacheNames = CacheConfig.CACHE1, cacheManager = "cacheManager1" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
Map<String, Person> personMap = new HashMap()<>;
//Key is first name, grouping all results by firstname
try {
personMap = dataClient.getPersonDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
}
#Cacheable(cacheNames = CacheConfig.CACHE2, cacheManager = "cacheManager2" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
List<PersonInfo> personMap = new ArrayList();
try {
personMap = dataClient.getPersonInfoDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
return transformPersonInfoToPerson(personMap);
}
}
The calling method:
#Service
#RequiredArgsConstructor
public class PersonService {
private final PersonCache personCache;
public List<Person> getPersonDetails(String firstName) {
Map<String, Person> personResponse1 = personCache.getPersonDetails(firstName);
//.. after fetching for the first result set, check for a flag and call the below cache to append the data
Map<String, Person> personResponse2 = personCache.getPersonInfoDetails(firstName);
personResponse1.putAll(personResponse2);
// This when returned at times does not contain any response from personResponse1 and only contains the personResponse2 data
return personResponse1.values();
}
}
Is it possible that the asynchronous API calls are causing some sort of miss , and the result set of the second cache is added to the result and returned ?
(The calling method is also called asynchronously from the controller class)
How should I handle to have the consistent response irrespective of the number of times the endpoint is triggered?
The cache key and value should be treated as immutable once they enter the cache. This is because they become available for multiple threads, so mutating an entry afterwards can become unpredictable. The behavior is less known when done in an unsafe way.
In your code the cached value is returned as the HashMap personResponse1. It is then modified to include the entries of personResponse2. At best this stores all of the contents in response1 for the next call, but it could also result in corruption as multiple threads write into it unsynchronized. When corrupted it may be that some entries cannot be found again, e.g. on resize they are not properly rehashed into the correct table location or are no longer on a bin's linked list. Another possibility is that since a mutable view of the values is returned to client code not shown, perhaps that code removes entries when processing it. The actual behavior becomes unpredictable, which is why it looks correct for a short time after refreshed because the cache discard the corrupted result.
A good practice would be to store an immutable Map.copy or decorated with Collections.unmodifiableMap. Then any mutations are disallowed and you would have caught this immediately. When consuming the cached responses merge them into a new map. Most likely your code was uncached so responses weren't stored and shared, but adding the cache here changed that so you now need to be mindful of the problems that arise with mutable shared state.
I have a spring class that when you call httpDatastoreFacade.getDatastore() it should give you the REST request thread safe datastore:
#Component
public class HttpDatastoreFacade {
private Boolean useAttribute = Boolean.FALSE;
public String getDatastore() {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextholder.currentRequestAttributes()).getRequest();
String datastore = request.getParameter("datastore");
if(useAttribute) {
datastore = String.valueOf(request.getAttribute("datastore"));
}
return datastore;
}
public void setDatastore(String datastore, Boolean useAttribute) {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextholder.currentRequestAttributes()).getRequest();
request.setAttribute("datastore", datastore);
this.useAttribute = useAttribute;
}
public Boolean getUseAttribute() {
return useAttribute;
}
}
Sometimes in my code I need to change that datastore but then I want to immediately change it back after I call whatever code needs the datastore differently:
#Component
public class someClass() {
#Autowired
private HttpDatastoreFacade datastoreFacade;
#Autowired
private OtherClass otherClass;
public void someMethod() {
String savedDatastore = datastoreFacade.getDatastore();
String savedUseAttribute = datastoreFacade.getUseAttribute;
//setDatastore to new settings
datastoreFacade.setDatastore("newStore", true);
//this is where I call my method's or methods that need this new datastore
otherClass.callSomeMethod();
//set the datastore back to old value
datastoreFacade.setDatastore(savedDatastore , savedUseAttribute );
}
}
My issue is that I'm running into threading problems where useAttribute is true but the datastore isn't set in the request attribute.
I'm looking for a better java pattern where I can lock the HttpDatastoreFacade while I do my otherClass.callSomeMethod() or whatever other calls I need to make until I set the HttpDatastoreFacade back to normal. otherCalss.callSomeMethod may be calling other methods that use HttpDatastoreFacade as well and they may want to set it how they need it. So maybe I need some short of datastore stack that is thread safe?
Seems a bean in #RequestScope could solve your problem.
#Component
#RequestScope
public class X {
//
}
you won't have to think about clearing the request scoped bean as you would the ThreadLocal. It will be collected when the corresponding ServletRequest is cleaned up.
I ended up making useAttribute a ThreadLocal variable which solved my problems.
private ThreadLocal<Boolean> useAttribute = new ThreadLocal<>();
I work on stress tests for REST server.
My aim is to create a mock controller method, which will throw 404 Error every 100 requests (other results are 200 OK), and check the total amount of sent requests and failed ones.
The problem is, even though I use ConcurrentHashMap and AtomicInteger for counting those figures, the amount of failed request varies +-20. Synchronization of RequestCounter.addFailed() didn't help. The only way I found is to synchronize controller's method, but it's not the option.
I run 220_000 stress test requests with 20 threads via Jmeter.
Here is my controller:
#RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(#RequestBody String data, Principal principal) {
RequestCounter.add();
if ((RequestCounter.getCounts().get("ADD").longValue() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
return ResponseEntity.ok().build();
}
}
The number of requests is counted here:
public class RequestCounter {
static Map<String, AtomicInteger> counts = new ConcurrentHashMap<>();
static {
counts.put("ADD", new AtomicInteger(0));
counts.put("ADD_FAILED", new AtomicInteger(0));
}
public static void add(){
counts.get("ADD_GDT").incrementAndGet();
}
public static void addFailed(){
counts.get("ADD_FAILED").incrementAndGet();
}
UPDATE
I followed an advice of javaguy and refactored the code by removing map and working with AtomicInteger variables directly. But the result is still unpredictable: failedRequestCount still varies from +-3
public class RequestCounter {
static AtomicInteger failedRequestsCounter = new AtomicInteger(0);
...
public static void addGDTFailed(){
failedRequestsCounter.incrementAndGet();
}
UPDATE2
The situation wasn't resolved neither by calling directly the thread-safe variable, nor by separation and synchronization of a method for getting modulus
The problem is RequestCounter class is not threadsafe because of these two lines:
counts.get("ADD_GDT").incrementAndGet();
counts.get("ADD_FAILED").incrementAndGet();
These are NOT atomic operations i.e., actually, the computation involves two steps (read the value from Map and then write). Though ConcurrentHashMap and AtomicInteger are individually threadsafe, but when you use them collectively, you need a synchronization or locking.
But you can achieve what you wanted for your testing with a much simpler code without using a ConcurrentHashMap itself.
To make the RequestCounter class threadsafe, just remove the Map, and directly access the AtomicInteger reference as below:
public class RequestCounter {
private final AtomicLong addInt = new AtomicLong();
private final AtomicLong addFailed = new AtomicLong();
public static long get() {
return addInt.get();
}
public static long add() {
return addInt.incrementAndGet();
}
public static long addFailed(){
return addFailed.incrementAndGet();
}
}
UPDATE1: Problem with 3% variation of requests:
You need to esnure that RequestCounter.add() is being called only once per request, look at my controller code below:
#RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(#RequestBody String data, Principal principal) {
if ((RequestCounter.get() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
RequestCounter.add();
return ResponseEntity.ok().build();
}
}
let's say we have a CountryList object in our application that should return the list of countries. The loading of countries is a heavy operation, so the list should be cached.
Additional requirements:
CountryList should be thread-safe
CountryList should load lazy (only on demand)
CountryList should support the invalidation of the cache
CountryList should be optimized considering that the cache will be invalidated very rarely
I came up with the following solution:
public class CountryList {
private static final Object ONE = new Integer(1);
// MapMaker is from Google Collections Library
private Map<Object, List<String>> cache = new MapMaker()
.initialCapacity(1)
.makeComputingMap(
new Function<Object, List<String>>() {
#Override
public List<String> apply(Object from) {
return loadCountryList();
}
});
private List<String> loadCountryList() {
// HEAVY OPERATION TO LOAD DATA
}
public List<String> list() {
return cache.get(ONE);
}
public void invalidateCache() {
cache.remove(ONE);
}
}
What do you think about it? Do you see something bad about it? Is there other way to do it? How can i make it better? Should i look for totally another solution in this cases?
Thanks.
google collections actually supplies just the thing for just this sort of thing: Supplier
Your code would be something like:
private Supplier<List<String>> supplier = new Supplier<List<String>>(){
public List<String> get(){
return loadCountryList();
}
};
// volatile reference so that changes are published correctly see invalidate()
private volatile Supplier<List<String>> memorized = Suppliers.memoize(supplier);
public List<String> list(){
return memorized.get();
}
public void invalidate(){
memorized = Suppliers.memoize(supplier);
}
Thanks you all guys, especially to user "gid" who gave the idea.
My target was to optimize the performance for the get() operation considering the invalidate() operation will be called very rare.
I wrote a testing class that starts 16 threads, each calling get()-Operation one million times. With this class I profiled some implementation on my 2-core maschine.
Testing results
Implementation Time
no synchronisation 0,6 sec
normal synchronisation 7,5 sec
with MapMaker 26,3 sec
with Suppliers.memoize 8,2 sec
with optimized memoize 1,5 sec
1) "No synchronisation" is not thread-safe, but gives us the best performance that we can compare to.
#Override
public List<String> list() {
if (cache == null) {
cache = loadCountryList();
}
return cache;
}
#Override
public void invalidateCache() {
cache = null;
}
2) "Normal synchronisation" - pretty good performace, standard no-brainer implementation
#Override
public synchronized List<String> list() {
if (cache == null) {
cache = loadCountryList();
}
return cache;
}
#Override
public synchronized void invalidateCache() {
cache = null;
}
3) "with MapMaker" - very poor performance.
See my question at the top for the code.
4) "with Suppliers.memoize" - good performance. But as the performance the same "Normal synchronisation" we need to optimize it or just use the "Normal synchronisation".
See the answer of the user "gid" for code.
5) "with optimized memoize" - the performnce comparable to "no sync"-implementation, but thread-safe one. This is the one we need.
The cache-class itself:
(The Supplier interfaces used here is from Google Collections Library and it has just one method get(). see http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Supplier.html)
public class LazyCache<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile Supplier<T> cache;
public LazyCache(Supplier<T> supplier) {
this.supplier = supplier;
reset();
}
private void reset() {
cache = new MemoizingSupplier<T>(supplier);
}
#Override
public T get() {
return cache.get();
}
public void invalidate() {
reset();
}
private static class MemoizingSupplier<T> implements Supplier<T> {
final Supplier<T> delegate;
volatile T value;
MemoizingSupplier(Supplier<T> delegate) {
this.delegate = delegate;
}
#Override
public T get() {
if (value == null) {
synchronized (this) {
if (value == null) {
value = delegate.get();
}
}
}
return value;
}
}
}
Example use:
public class BetterMemoizeCountryList implements ICountryList {
LazyCache<List<String>> cache = new LazyCache<List<String>>(new Supplier<List<String>>(){
#Override
public List<String> get() {
return loadCountryList();
}
});
#Override
public List<String> list(){
return cache.get();
}
#Override
public void invalidateCache(){
cache.invalidate();
}
private List<String> loadCountryList() {
// this should normally load a full list from the database,
// but just for this instance we mock it with:
return Arrays.asList("Germany", "Russia", "China");
}
}
Whenever I need to cache something, I like to use the Proxy pattern.
Doing it with this pattern offers separation of concerns. Your original
object can be concerned with lazy loading. Your proxy (or guardian) object
can be responsible for validation of the cache.
In detail:
Define an object CountryList class which is thread-safe, preferably using synchronization blocks or other semaphore locks.
Extract this class's interface into a CountryQueryable interface.
Define another object, CountryListProxy, that implements the CountryQueryable.
Only allow the CountryListProxy to be instantiated, and only allow it to be referenced
through its interface.
From here, you can insert your cache invalidation strategy into the proxy object. Save the time of the last load, and upon the next request to see the data, compare the current time to the cache time. Define a tolerance level, where, if too much time has passed, the data is reloaded.
As far as Lazy Load, refer here.
Now for some good down-home sample code:
public interface CountryQueryable {
public void operationA();
public String operationB();
}
public class CountryList implements CountryQueryable {
private boolean loaded;
public CountryList() {
loaded = false;
}
//This particular operation might be able to function without
//the extra loading.
#Override
public void operationA() {
//Do whatever.
}
//This operation may need to load the extra stuff.
#Override
public String operationB() {
if (!loaded) {
load();
loaded = true;
}
//Do whatever.
return whatever;
}
private void load() {
//Do the loading of the Lazy load here.
}
}
public class CountryListProxy implements CountryQueryable {
//In accordance with the Proxy pattern, we hide the target
//instance inside of our Proxy instance.
private CountryQueryable actualList;
//Keep track of the lazy time we cached.
private long lastCached;
//Define a tolerance time, 2000 milliseconds, before refreshing
//the cache.
private static final long TOLERANCE = 2000L;
public CountryListProxy() {
//You might even retrieve this object from a Registry.
actualList = new CountryList();
//Initialize it to something stupid.
lastCached = Long.MIN_VALUE;
}
#Override
public synchronized void operationA() {
if ((System.getCurrentTimeMillis() - lastCached) > TOLERANCE) {
//Refresh the cache.
lastCached = System.getCurrentTimeMillis();
} else {
//Cache is okay.
}
}
#Override
public synchronized String operationB() {
if ((System.getCurrentTimeMillis() - lastCached) > TOLERANCE) {
//Refresh the cache.
lastCached = System.getCurrentTimeMillis();
} else {
//Cache is okay.
}
return whatever;
}
}
public class Client {
public static void main(String[] args) {
CountryQueryable queryable = new CountryListProxy();
//Do your thing.
}
}
Your needs seem pretty simple here. The use of MapMaker makes the implementation more complicated than it has to be. The whole double-checked locking idiom is tricky to get right, and only works on 1.5+. And to be honest, it's breaking one of the most important rules of programming:
Premature optimization is the root of
all evil.
The double-checked locking idiom tries to avoid the cost of synchronization in the case where the cache is already loaded. But is that overhead really causing problems? Is it worth the cost of more complex code? I say assume it is not until profiling tells you otherwise.
Here's a very simple solution that requires no 3rd party code (ignoring the JCIP annotation). It does make the assumption that an empty list means the cache hasn't been loaded yet. It also prevents the contents of the country list from escaping to client code that could potentially modify the returned list. If this is not a concern for you, you could remove the call to Collections.unmodifiedList().
public class CountryList {
#GuardedBy("cache")
private final List<String> cache = new ArrayList<String>();
private List<String> loadCountryList() {
// HEAVY OPERATION TO LOAD DATA
}
public List<String> list() {
synchronized (cache) {
if( cache.isEmpty() ) {
cache.addAll(loadCountryList());
}
return Collections.unmodifiableList(cache);
}
}
public void invalidateCache() {
synchronized (cache) {
cache.clear();
}
}
}
I'm not sure what the map is for. When I need a lazy, cached object, I usually do it like this:
public class CountryList
{
private static List<Country> countryList;
public static synchronized List<Country> get()
{
if (countryList==null)
countryList=load();
return countryList;
}
private static List<Country> load()
{
... whatever ...
}
public static synchronized void forget()
{
countryList=null;
}
}
I think this is similar to what you're doing but a little simpler. If you have a need for the map and the ONE that you've simplified away for the question, okay.
If you want it thread-safe, you should synchronize the get and the forget.
What do you think about it? Do you see something bad about it?
Bleah - you are using a complex data structure, MapMaker, with several features (map access, concurrency-friendly access, deferred construction of values, etc) because of a single feature you are after (deferred creation of a single construction-expensive object).
While reusing code is a good goal, this approach adds additional overhead and complexity. In addition, it misleads future maintainers when they see a map data structure there into thinking that there's a map of keys/values in there when there is really only 1 thing (list of countries). Simplicity, readability, and clarity are key to future maintainability.
Is there other way to do it? How can i make it better? Should i look for totally another solution in this cases?
Seems like you are after lazy-loading. Look at solutions to other SO lazy-loading questions. For example, this one covers the classic double-check approach (make sure you are using Java 1.5 or later):
How to solve the "Double-Checked Locking is Broken" Declaration in Java?
Rather than just simply repeat the solution code here, I think it is useful to read the discussion about lazy loading via double-check there to grow your knowledge base. (sorry if that comes off as pompous - just trying teach to fish rather than feed blah blah blah ...)
There is a library out there (from atlassian) - one of the util classes called LazyReference. LazyReference is a reference to an object that can be lazily created (on first get). it is guarenteed thread safe, and the init is also guarenteed to only occur once - if two threads calls get() at the same time, one thread will compute, the other thread will block wait.
see a sample code:
final LazyReference<MyObject> ref = new LazyReference() {
protected MyObject create() throws Exception {
// Do some useful object construction here
return new MyObject();
}
};
//thread1
MyObject myObject = ref.get();
//thread2
MyObject myObject = ref.get();
This looks ok to me (I assume MapMaker is from google collections?) Ideally you wouldn't need to use a Map because you don't really have keys but as the implementation is hidden from any callers I don't see this as a big deal.
This is way to simple to use the ComputingMap stuff. You only need a dead simple implementation where all methods are synchronized, and you should be fine. This will obviously block the first thread hitting it (getting it), and any other thread hitting it while the first thread loads the cache (and the same again if anyone calls the invalidateCache thing - where you also should decide whether the invalidateCache should load the cache anew, or just null it out, letting the first attempt at getting it again block), but then all threads should go through nicely.
Use the Initialization on demand holder idiom
public class CountryList {
private CountryList() {}
private static class CountryListHolder {
static final List<Country> INSTANCE = new List<Country>();
}
public static List<Country> getInstance() {
return CountryListHolder.INSTANCE;
}
...
}
Follow up to Mike's solution above. My comment didn't format as expected... :(
Watch out for synchronization issues in operationB, especially since load() is slow:
public String operationB() {
if (!loaded) {
load();
loaded = true;
}
//Do whatever.
return whatever;
}
You could fix it this way:
public String operationB() {
synchronized(loaded) {
if (!loaded) {
load();
loaded = true;
}
}
//Do whatever.
return whatever;
}
Make sure you ALWAYS synchronize on every access to the loaded variable.