return "constant" value from method java - java

I'm not quite sure if this is possible so that's why I ask you guys. I want to write a method that knows if it has been visited before and if it has return the same value it has lasttime it were visited. I can't use fields/instance varibles for this.
This is want I want to do, without the instance variable foo:
private FooObject foo = null;
public int setFoo(FooObject in)
{
if(foo == null)
foo = in;
return foo.getX();
}
Could this be done?

that knows if it has been visited before
Knowledge of what happened before requires memory, aka state. Therefore you will need something to store that knowledge; you can't avoid it.

I agree with #Oli Charlesworth, you cannot do this without state. But using a field to save state is just one of many options. You could, for instance, save the state as a system property using System.setProperty() and System.getProperty(), or save the state to a file. You could even save the state to a database.

Without using an instance variable? no, it can't be done. You need a way for the method to "remember" something between calls - a local variable won't work, it has to be something that "lives" outside a method call. And that's what an instance variable is for: saving state, visible from all instance methods.

You can use a cache. Read from cache for looking for the input. If the input is already in cache, then return the value from cache. If don't, put the entry in cache and return the new calculated value.
CacheManager singletonManager = CacheManager.create();
Cache memoryOnlyCache = new Cache("testCache", 5000, false, false, 5, 2);
manager.addCache(memoryOnlyCache);
Cache cache = singletonManager.getCache("testCache");
Put new entry
Element element = new Element("key1", "value1");
cache.put(element);
Looking for in chache:
Element element = cache.get("key1");
Object value = element.getObjectValue();
For remove:
cache.remove("key1");
You can persist this to disk.
For more information, see in http://ehcache.org/

You could use the Preferences API. That allows you to store memory across application loads though, so if you want it to be restarted each time you load, you'll need to set a shutdown hook to clear the value from the Preferences API.

Related

ConcurrentMap on demand loading java

I'm working on an on demand cache that needs to be thread-safe. I have data for about 30K + items (in one file) that I want to obtain only when needed for my mult-threaded game. However I'm not sure if my approach is how ConcurrentMap's computeIfAbsent is supposed to be used, and if it isn't what alternative is there for me to lazily load contents from a single file without worrying about threading issues? I want to avoid locking if the object exists in my map, which I've read using CHM does on reads.
I've pre-cached file names (which are IDs) that I want to load to ensure they exist to avoid constant checking via the headers hash map. The headers map is read-only and will only be loaded once upon starting of my program.
this is what I've done:
private static final ConcurrentMap<Integer, ItemData> items = new ConcurentHashMap<>();
private static final HashMap<Integer, Byte> headers = new HashMap<>(); // pre loaded file names to avoid checking if file exists
public static ItemData getItem(int itemID) {
var item = items.get(itemID);
if (item != null) {
return item;
}
// if item doesn't exist in map, check if it exists in file on disk
if (!headers.containsKey(itemID)) {
return null;
}
// if item exists in file add it to cache
return items.computeIfAbsent(itemID, k -> {
try (var dis = new DataInputStream(new FileInputStream("item.bin"))) {
var data = new ItemData(itemID);
data.load(dis); // obtains only data for one item
return item;
} catch (IOException e) {
// ommited for brevity. logging goes here.
return null;
}
});
}
Update: Pre-loading isn't an option for me, I agree doing that would solve threading-issues as it will only be read-only. But my game assets combined have a total size of over 2GB. I don't want to load everything during start up as some items in the files may never be used. Thus I'm looking for an approach to load them only when needed.
You wrote
I want to avoid locking if the object exists in my map, which I've read using CHM does on reads.
I don’t know where you read that but it’s definitely wrong. It’s not even an outdated statement as even the very first version specifies:
Retrieval operations (including get) generally do not block…
The general structure of your approach is fine. In case of concurrent first time accesses for a key, it’s possible that multiple threads pass the first check but only one will do the actual retrieval in computeIfAbsent and all of them will use the result. Subsequent accesses to an already loaded item may benefit from the first plain get access.
There’s still something to improve.
return items.computeIfAbsent(itemID, k -> {
try (var dis = new DataInputStream(new FileInputStream("item.bin"))) {
var data = new ItemData(k);
data.load(dis); // obtains only data for one item
return item;
} catch (IOException e) {
// may still do logging here
throw new UncheckIOException(e);
}
});
First, while it’s a good approach to do logging (which you omitted for brevity), returning null and forcing the calling code to deal with null is not a good idea. You already have the headers.containsKey(…) check that tells us that the resource is supposed to be there, so the application likely has no way to deal with the absence, so we’re talking about an exceptional situation.
Further, you can use the k parameter passed to the function rather than accessing itemID from the surrounding scope. Limiting access scopes is not only cleaner, in this case, it turns the lambda expression into a non-capturing one, which means that it doesn’t require to create a new object each time, that would otherwise be needed to hold the captured value.
If you really read the same item.bin file for all ItemData, you may consider using memory mapped I/O to share the data, instead of reading it with a DataInputStream. The ByteBuffer representation of a memory mapped file offers almost the same methods to get compound items, it even supports little endian processing that DataInputStream doesn’t support.

Java garbage collection in multithreaded application for local variable

I have following use-case:
Need a single background thread which maintains a set of accountIDs in memory and it fetches the latest accountIds every 1 second
Other multiple parallel running process will search if particular accountID is present in above Set or not
To achieve above use-case, I have following code. runTask() is a method which is responsible for fetching new Set every 1 second. doesAccountExist method is called by other parallel threads to check if accountId exists in the Set or not.
class AccountIDFetcher {
private Set<String> accountIds;
private ScheduledExecutorService scheduledExecutorService;
public AccountIDFetcher() {
this.accountIds = new HashSet<String>();
scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
scheduledExecutorService.scheduleWithFixedDelay(this::runTask, 0, 1, TimeUnit.SECONDS);
}
// Following method runs every 1 second
private void runTask() {
accountIds = getAccountIds()
}
// other parallel thread calls below method
public boolean doesAccountExist(String accountId) {
return accountIds.contains(instanceId);
}
private Set<String> getAccountIds() {
Set<String> accounts = new HashSet<String>();
// calls Database and put list of accountIds into above set
return accounts;
}
}
I have following question
In runTask method, I just change the reference of accountIds variable to a new object. So if, Thread-2 is in the middle of searching accountId in doesAccountExist() method and at the same time if Thread-1 executes runTask() & changes the reference of accountIds variable to a new object then old object gets orphaned. is it possible that old object can be garbage collected before Thread-2 finish searching in it?
tl;dr
You asked:
is it possible that old object can be garbage collected before Thread-2 finish searching in it?
No, the old Set object does not become garbage while some thread is still using it.
An object only becomes a candidate for garbage-collection after each and every reference to said object (a) goes out of scope, (b) is set to null, or (c) is a weak reference.
No, an object in use within a method will not be garbage-collected
In Java, an object reference assignment is atomic, as discussed in another Question. When this.accountIds is directed to point to a new Set object, that happens in one logical operation. That means that any other code in any other thread accessing the accountIds member field will always successfully access either the old Set object or the new Set object, always one or the other.
If during that re-assignment another thread accessed the old Set object, that other thread's code is working with a copy of the object reference. You can think of your doesAccountExist method:
public boolean doesAccountExist(String accountId) {
return accountIds.contains(accountId);
}
…as having a local variable with a copy of the object reference, as if written like this:
public boolean doesAccountExist(String accountId) {
Set<String> set = this.accountIds ;
return set.contains(accountId);
}
While one thread is replacing the reference to a new Set on the member field accountIds, the doesAccountExist method already has a copy of the reference to the old Set. At that moment, while one thread is changing the member field reference, and another thread has a local reference, the garbage collector sees both the new and old Set objects as having (at least) one reference each. So neither is a candidate for being garbage-collected.
Actually, more technically correct would be explaining that at the point in your line return accountIds.contains(accountId); where execution reaches the accountIds portion, the current (old) Set will be accessed. A moment later the contains method begins its work, during which re-assigning a new Set to that member field has no effect on this method's work-in-progress already using the old Set.
This means that even after the new Set has been assigned in one thread, the other thread may still be continuing its work of searching the old Set. This may or may not be a problem depending on the business context of your app. But your Question did not address this stale-data transactional aspect.
So regarding your question:
is it possible that old object can be garbage collected before Thread-2 finish searching in it?
No, the old Set object does not become garbage while some thread is still using it.
Other issues
Your code does have other issues.
Visibility
You declared your member field as private Set<String> accountIds;. If you access that member field across threads on a host machine with multiple cores, then you have a visibility problem. The caches on each core may not be refreshed immediately when you assign a different object to that member field. As currently written, it is entirely possible that one thread accessing this.accountIds will gain access to the old Set object even after that variable was assigned the new Set object.
If you do not already know about the issues I mention, study up on concurrency. There is more involved than I can cover here. Learn about the Java Memory Model. And I strongly recommend reading and re-reading the classic book, Java Concurrency in Practice by Brian Goetz, et al.
volatile
One solution is to mark the member field as volatile. So, this:
private Set<String> accountIds;
…becomes this:
volatile private Set<String> accountIds;
Marking as volatile avoids a stale cache on a CPU core pointing to the old object reference rather than the new object reference.
AtomicReference
Another solution is using an object of AtomicReference class as the member field. I would mark it final so that one and only one such object is ever assigned to that member field, so the field is a constant rather than a variable. Then assign each new Set object as the payload contained within that AtomicReference object. Code wanting the current Set object calls a getter method on that AtomicReference object. This call is guaranteed to be thread-safe, eliminating the need for volatile.
Concurrent access to existing Set
Another possible problem with your code might be concurrent access to an existing Set. If you have more than one thread accessing the existing Set, then you must protect that resource.
One way to protect access to that Set is to use a thread-safe implementation of Set such as ConcurrentSkipListSet.
From what you have showed in the Question, the only access to the existing Set that I noticed is calling contains. If you are never modifying the existing Set, then merely calling contains across multiple threads may be safe — I just don't know, you'd have to research it.
If you intend to never modify an existing Set, then you can enforce that by using an unmodifiable set. One way to produce an unmodifiable set is to construct and populate a regular set. Then feed that regular set to the method Set.copyOf. So your getAccountIds method would look like this:
private Set<String> getAccountIds() {
Set<String> accounts = new HashSet<String>();
// calls Database and put list of accountIds into above set
return Set.copyOf( accounts );
}
Return a copy rather a reference
There are two easy ways to avoid dealing with concurrency:
Make the object immutable
Provide a copy of the object
As for the first way, immutability, the Java Collections Framework is generally very good but unfortunately lacks explicit mutability & immutability in its type system. The Set.of methods and Collections.unmodifiableSet both provide a Set that cannot be modified. But the type itself does not proclaim that fact. So we cannot ask the compiler to enforce a rule such as our AtomicReference only storing an immutable set. As an alternative, consider using a third-party collections with immutability as part of its type. Perhaps Eclipse Collections or Google Guava.
As for the second way, we can make a copy of our Set of account IDs whenever needing access. So we need a getCurrentAccountIds method that goes into the AtomicReference, retrieves the Set stored there, and called Set.copyOf to produce a new set of the same contained objects. This copy operation is not documented as being thread-safe. So we should mark the method synchronized to allow only one copy operation at a time. Bonus: We can mark this method public to give any calling programmer access to the Set of account IDs for their own perusal.
synchronized public Set < UUID > getCurrentAccountIds ( )
{
return Set.copyOf( this.accountIdsRef.get() ); // Safest approach is to return a copy rather than original set.
}
Our convenience method doesAccountExist should call that same getCurrentAccountIds to obtain a copy of the set before doing its "contains" logic. This way we do not care whether or not the "contains" work is thread-safe.
Caveat: I am not satisfied with using Set.copyOf to as means to avoid any possible thread-safety issues. That method notes that if the passed collection being copied is already an unmodifiable set, then a copy may not be made. In real work I would use a Set implementation that guarantees thread-safety whether found bundled with Java or by adding a third-party library.
Do not use object within constructor
I do not like seeing the scheduled executor service appearing within your constructor. I see two issues there: (a) app lifecycle and (b) using an object within a constructor.
Creating the executor service, scheduling tasks on that service, and shutting down that service are all related to the lifecycle of the app. An object generally should not be aware of its lifecycle within the greater app. This account IDs provider object should know only how to do its job (provide IDs) but should not be responsible for putting itself to work. So your code is mixing responsibilities, which is generally a poor practice.
Another problem is that the executor service is being scheduled to immediately start using the very object that we are still constructing. Generally, the best practice is to not use an object while still under construction. You may get away with such use, but doing so is risky and is prone to leading to bugs. A constructor should be short and sweet, used just to validate inputs, establish required resources, and ensure the integrity of the object being birthed.
I did not pull the service out of your constructor only because I did not want to this Answer to go too far out into the weeds. However, I did make two adjustments. (a) I changed the initial delay on the scheduleWithFixedDelay call from zero to one second. This is a hack to give the constructor time to finish birthing the object before its first use. (b) I added the tearDown method to properly shutdown the executor service so its backing thread-pool does not continue running indefinitely in zombie fashion.
Tips
I suggest renaming your getAccountIds() method. The get wording in Java is usually associated with the JavaBeans convention of accessing an existing property. In your case you are generating an entirely new replacement set of values. So I would change that name to something like fetchFreshAccountIds.
Consider wrapping your scheduled task with a try-catch. Any Exception or Error bubbling up to reach the ScheduledExecutorService brings a silent halt to any further scheduling. See ScheduledExecutorService Exception handling.
Example code.
Here is a complete example of my take on your code.
Caveat: Use at your own risk. I am not a concurrency expert. This is meant as food-for-thought, not production use.
I used UUID as the data type of the account IDs to be more realistic and clear.
I changed some of your class & method names for clarity.
Notice which methods are private and which are public.
package work.basil.example;
import java.time.Duration;
import java.time.Instant;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class AccountIdsProvider
{
// Member fields
private AtomicReference < Set < UUID > > accountIdsRef;
private ScheduledExecutorService scheduledExecutorService;
// Constructor
public AccountIdsProvider ( )
{
this.accountIdsRef = new AtomicReference <>();
this.accountIdsRef.set( Set.of() ); // Initialize to empty set.
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay( this :: replaceAccountIds , 1 , 1 , TimeUnit.SECONDS ); // I strongly suggest you move the executor service and the scheduling work to be outside this class, to be a different class’ responsibility.
}
// Performs database query to find currently relevant account IDs.
private void replaceAccountIds ( )
{
// Beware: Any uncaught Exception or Error bubbling up to the scheduled executor services halts the scheduler immediately and silently.
try
{
System.out.println( "Running replaceAccountIds. " + Instant.now() );
Set < UUID > freshAccountIds = this.fetchFreshAccountIds();
this.accountIdsRef.set( freshAccountIds );
System.out.println( "freshAccountIds = " + freshAccountIds + " at " + Instant.now() );
}
catch ( Throwable t )
{
t.printStackTrace();
}
}
// Task to be run by scheduled executor service.
private Set < UUID > fetchFreshAccountIds ( )
{
int limit = ThreadLocalRandom.current().nextInt( 0 , 4 );
HashSet < UUID > uuids = new HashSet <>();
for ( int i = 1 ; i <= limit ; i++ )
{
uuids.add( UUID.randomUUID() );
}
return Set.copyOf( uuids ); // Return unmodifiable set.
}
// Calling programmers can get a copy of the set of account IDs for their own perusal.
// Pass a copy rather than a reference for thread-safety.
// Synchronized in case copying the set is not thread-safe.
synchronized public Set < UUID > getCurrentAccountIds ( )
{
return Set.copyOf( this.accountIdsRef.get() ); // Safest approach is to return a copy rather than original set.
}
// Convenience method for calling programmers.
public boolean doesAccountExist ( UUID accountId )
{
return this.getCurrentAccountIds().contains( accountId );
}
// Destructor
public void tearDown ( )
{
// IMPORTANT: Always shut down your executor service. Otherwise the backing pool of threads may run indefinitely, like a zombie 🧟‍.
if ( Objects.nonNull( this.scheduledExecutorService ) )
{
System.out.println( "INFO - Shutting down the scheduled executor service. " + Instant.now() );
this.scheduledExecutorService.shutdown(); // I strongly suggest you move the executor service and the scheduling work to be outside this class, to be a different class’ responsibility.
}
}
public static void main ( String[] args )
{
System.out.println( "INFO - Starting app. " + Instant.now() );
AccountIdsProvider app = new AccountIdsProvider();
try { Thread.sleep( Duration.ofSeconds( 10 ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
app.tearDown();
System.out.println( "INFO - Ending app. " + Instant.now() );
}
}
Garbage collection is not the main issue with this code. The lack of any synchronization is the main issue.
If thread-2 is "searching" in something, it necessarily has a reference to that thing, so it's not going to get GC'd.
Why not use 'synchronized' so you can be sure of what will happen?
garbage collection wise, you will not get into any surprise, but not because the accepted answer implies. It is somehow more tricky.
May be visualizing this will help.
accountIds ----> some_instance_1
Suppose ThreadA is now working with some_instance_1. It started to search for accountId in it. While that operation is going, ThreadB changes what that reference is pointing to. So it becomes:
some_instance_1
accountIds ----> some_instance_2
Because reference assign is atomic, this is what ThreadA will see also, if it reads that reference again. At this point in time, some_instance_1 is eligible for garbage collection, since no one refers to it. Just take note, that this will only happen if ThreadA sees this write that ThreadB did. Either way : you are safe (gc wise only), because ThreadA either works with a stale copy (which you said it's OK) or the most recent one.
This does not mean that everything is fine with your code.
What that answer gets right indeed is that reference assigning is atomic, so once a thread writes to a reference (accountIds = getAccountIds()), a reading thread (accountIds.contains(instanceId);) that will indeed perform the read will see the write. I say "indeed" because an optimizer might not even issue such a read, to begin with. In very simple (and somehow wrong) words, each thread might get its own copy of accountIds and because that is a "plain" read without any special semantics (like volatile, release/acquire, synchronization, etc), reading threads have no obligation to see the writing thread action.
So, even if someone actually did accountIds = getAccountIds(), it does not mean that reading threads will see this. And it gets worse. This write might not ever be seen. You need to introduce special semantics if you want guarantees (and you absolutely do).
For that you need to make your Set volatile:
private volatile Set<String> accountIds = ...
so that when multi threads are involved, you would get the needed visibility guarantees.
Then not to interfere with any in-flight updates of accountIds, you can simply work on a local copy of it:
public boolean doesAccountExist(String accountId) {
Set<String> local = accountIds;
return local.contains(accountId);
}
Even if accountIds change while you are in this method, you do not care about that change, since you are searching against local, which is unaware of the change.

Java ehCache, how to replace members

I have a simple method that I have annotated for caching.
#Cacheable(value = "devices", key = "#hardwareId", unless = "#result == null")
public Device get(String hardwareId)
I have a mechanism to know when someone changes the underlying database. So that I know to Evict a member from the cache, so that the next call in will go back to the database.
getCache().remove(hardwareId);
What I would like to do it REPLACE the element in the cache. The reason for this is that the call back to the database can take 1000ms & I'd like to not have that blip on the performance of the method.
As far as I can tell I have two options.
Option 1:
When I evict the member, call back into the service at that time.
getCache().remove(hardwareId);
service.get(hardwareId);
Option 2:
Create an instance of 'net.sf.ehcache.bootstrap.BootstrapCacheLoader'
that registers on startup the same class to be notified element
being removed from a cache (notifyElementRemoved()).
On #PostContruct get all methods annotated with #Cacheable. Create a Map of
'cacheName' to Method instance (java reflection Method)
When notifyElementRemoved() is triggered, uses the cache name to get the Method instance, with that invoke it to trigger the cache to be repopulated.
Method method = map.get(cacheName);
// Add magic here to get the service.
Object serviceInstance = applicationContext.getBean("deviceService");
if (Proxy.isProxyClass(serviceInstance.getClass())) {
Proxy.getInvocationHandler(serviceInstance).invoke(serviceInstance, method, new Object[] {objectKey});
} else {
method.invoke(serviceInstance, objectKey);
}
The downside of option 1 is that I have to go modify 30+ classes to put in the logic to call back into the service.
The downside of option 2 is that it's a bit complex, it feels like it would be good if ehCache could provide this feature. It knows what method it wrapped, it knows what the key/parameters were that called into this method.
The downside of both options is that there will always be a time when the cache does not contain the member & could cause a blip in performance.
My question is, does ehCache provide the feature I want or is there another mechanism out there to do REPLACEMENT of members in the cache with zero time of the cache being empty?
Don't do option 2. Too complicated. In general, the way it goes is to have a #Cacheable and a #CachePut method. Why not using that?
#Cacheable(value = "devices", key = "#hardwareId", unless = "#result == null")
public Device get(String hardwareId)
#CachePut(value ="devices", key= "#hardwardId", unless = "#result == null")
public Device update(String hardwareId)
It should cleanly solve your problem.
BTW, you don't need to specify the key. It is implicit.

Is it appropriate to use AtomicReference.compareAndSet to set a reference to the results of a database call?

I am implementing a simple cache with the cache stored as an AtomicReference.
private AtomicReference<Map<String, String>> cacheData;
The cache object should be populated (lazily) from a database table.
I provide a method to return the cache data to a caller, but if the data is null (ie. not loaded), then the code needs to load the data from the database. To avoid synchronized I thought of using the compareAndSet() method:
public Object getCacheData() {
cacheData.compareAndSet(null, getDataFromDatabase()); // atomic reload only if data not set!
return Collections.unmodifiableMap(cacheData.get());
}
Is it ok to use compareAndSet in this way ie. to involve a database call as part of the atomic action? Is it any better/worse than just synchronizing the method?
Many thanks for any advice..
You do not achieve expected behaviour. This expression:
cacheData.compareAndSet(null, getDataFromDatabase())
will always call getDataFromDatabase() first. This means that it doesn't matter if the data was cached or not. If it was, you still call the database, but discard the results. The cache is working, but the performance is equally poor.
Consider this instead:
if(cacheData.get() == null) {
cacheData.compareAndSet(null, unmodifiableMap(getDataFromDatabase()));
}
return cacheData.get());
It's not perfect (still getDataFromDatabase() can be called multiple times at the beginning), but will work later as expected. Also I moved Collections.unmodifiableMap() earlier so that you don't have to wrap the same map over and over again.
Which brings us to even simpler implementation (no synchronized or AtomicReference needed):
private volatile Map<String, String> cacheData;
if(cacheData == null) {
cacheData = unmodifiableMap(getDataFromDatabase());
}
return cacheData;

How to use ReadWriteLock?

I'm the following situation.
At web application startup I need to load a Map which is thereafter used by multiple incoming threads. That is, requests comes in and the Map is used to find out whether it contains a particular key and if so the value (the object) is retrieved and associated to another object.
Now, at times the content of the Map changes. I don't want to restart my application to reload the new situation. Instead I want to do this dynamically.
However, at the time the Map is re-loading (removing all items and replacing them with the new ones), concurrent read requests on that Map still arrive.
What should I do to prevent all read threads from accessing that Map while it's being reloaded ? How can I do this in the most performant way, because I only need this when the Map is reloading which will only occur sporadically (each every x weeks) ?
If the above is not an option (blocking) how can I make sure that while reloading my read request won't suffer from unexpected exceptions (because a key is no longer there, or a value is no longer present or being reloaded) ?
I was given the advice that a ReadWriteLock might help me out. Can you someone provide me an example on how I should use this ReadWriteLock with my readers and my writer ?
Thanks,
E
I suggest to handle this as follow:
Have your map accessible at a central place (could be a Spring singleton, a static ...).
When starting to reload, let the instance as is, work in a different Map instance.
When that new map is filled, replace the old map with this new one (that's an atomic operation).
Sample code:
static volatile Map<U, V> map = ....;
// **************************
Map<U, V> tempMap = new ...;
load(tempMap);
map = tempMap;
Concurrency effects :
volatile helps with visibility of the variable to other threads.
While reloading the map, all other threads see the old value undisturbed, so they suffer no penalty whatsoever.
Any thread that retrieves the map the instant before it is changed will work with the old values.
It can ask several gets to the same old map instance, which is great for data consistency (not loading the first value from the older map, and others from the newer).
It will finish processing its request with the old map, but the next request will ask the map again, and will receive the newer values.
If the client threads do not modify the map, i.e. the contents of the map is solely dependent on the source from where it is loaded, you can simply load a new map and replace the reference to the map your client threads are using once the new map is loaded.
Other then using twice the memory for a short time, no performance penalty is incurred.
In case the map uses too much memory to have 2 of them, you can use the same tactic per object in the map; iterate over the map, construct a new mapped-to object and replace the original mapping once the object is loaded.
Note that changing the reference as suggested by others could cause problems if you rely on the map being unchanged for a while (e.g. if (map.contains(key)) {V value = map.get(key); ...}. If you need that, you should keep a local reference to the map:
static Map<U,V> map = ...;
void do() {
Map<U,V> local = map;
if (local.contains(key)) {
V value = local.get(key);
...
}
}
EDIT:
The assumption is that you don't want costly synchronization for your client threads. As a trade-off, you allow client threads to finish their work that they've already begun before your map changed - ignoring any changes to the map that happened while it is running. This way, you can safely made some assumptions about your map - e.g. that a key is present and always mapped to the same value for the duration of a single request. In the example above, if your reader thread changed the map just after a client called map.contains(key), the client might get null on map.get(key) - and you'd almost certainly end this request with a NullPointerException. So if you're doing multiple reads to the map and need to do some assumptions as the one mentioned before, it's easiest to keep a local reference to the (maybe obsolete) map.
The volatile keyword isn't strictly necessary here. It would just make sure that the new map is used by other threads as soon as you changed the reference (map = newMap). Without volatile, a subsequent read (local = map) could still return the old reference for some time (we're talking about less than a nanosecond though) - especially on multicore systems if I remember correctly. I wouldn't care about it, but f you feel a need for that extra bit of multi-threading beauty, your free to use it of course ;)
I like the volatile Map solution from KLE a lot and would go with that. Another idea that someone might find interesting is to use the map equivalent of a CopyOnWriteArrayList, basically a CopyOnWriteMap. We built one of these internally and it is non-trivial but you might be able to find a COWMap out in the wild:
http://old.nabble.com/CopyOnWriteMap-implementation-td13018855.html
This is the answer from the JDK javadocs for ReentrantReadWriteLock implementation of ReadWriteLock. A few years late but still valid, especially if you don't want to rely only on volatile
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}

Categories