I have a web app that will access a distributed map via the Hazelcast client on each doPost() to the servlet. My question is should I use a Singleton to access the client from all the different servlets, and is this thread safe?
This is my class Singleton class:
public class HazelcastDistributedCacheClientSingleton {
//Properties from FrameworkResources.properties file.
private static String hazelcastEnvironmentName = null;
private static final String mapName = "wfc";
//COnstructor
private HazelcastDistributedCacheClientSingleton() {
}
//Get the Map
public static IMap<String, Object> getMap() {
// Make sure we have a name for the IMap
if (hazelcastEnvironmentName == null) {
hazelcastEnvironmentName = "DEV";
}
return getHazelcastClient().getMap(mapName);
}
public static Object getSessionObj(String key){
return getMap().get(key);
}
public static void setSessionObj(String key, Object value){
getMap().set(key, value);
}
public static void removeSessionObj(String key){
getMap().remove(key);
}
//The client singleton
private static HazelcastInstance hzClientInstance = null;
// Get the client singleton
public static HazelcastInstance getHazelcastClient() {
// Create a client instance if not already exists
if (hzClientInstance == null) {
// Create a synchronized block
synchronized (new Object()) {
// Double check that we did not do this in another thread
if (hzClientInstance == null) {
//Get startup parms from config file
try {
hazelcastEnvironmentName = java.util.ResourceBundle.getBundle("FrameworkResources").getString("HazelcastEnvironmentName");
} catch (MissingResourceException e) {
hazelcastEnvironmentName = "DEV";
}
// Configure Client
ClientConfig clientConfig = new ClientConfig();
// Set the group name to segregate dev, test, prod caches
clientConfig.getGroupConfig().setName(hazelcastEnvironmentName).setPassword(hazelcastEnvironmentName);
hzClientInstance = HazelcastClient.newHazelcastClient(clientConfig);
}
} //end synchronized block
}
// Return the client instance
return hzClientInstance;
}
}
HazelcastInstances (client or server) are threadsafe and are designed to be shared between threads. It is best to make it a singleton.
A few comments:
1: make sure hzClientInstance is volatile. Otherwise you will have a broken the double checked locking implementation. Probably you also want to make hazelcastEnvironmentName volatile.
2: you can't synchronize on synchronized new Object()) since this object will not be shared between threads. I would synchronize on 'this' or create a lock object as final field in the HazelcastDistributedCacheClientSingleton
3: don't retrieve the IMap every time you need it. Put it in a (volatile) field the first time you retrieve it.
Related
I have a situation where I read data from a YAML file that is important for the application because it is used in several classes. Here is my code:
public class CredentialsReader {
private UserCredentials credentials;
private boolean isReading = false;
public CredentialsReader() {
}
public void readCredentials() {
Runnable readerTask = new Runnable() {
#Override
public void run() {
isReading = true;
parseCredentials();
isReading = false;
System.err.println("Parsed credentials");
}
};
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(readerTask, 0, 60, TimeUnit.SECONDS);
}
private void parseCredentials() {
final File f = new File("/home/dev/IdeaProjects/server/src/main/resources/credentials.yaml");
try {
UserCredentials userCredentials = new ObjectMapper().readValue(f, UserCredentials.class);
this.credentials = userCredentials;
System.out.println(this.credentials.getUsername() + ", " + this.credentials.getPassword());
} catch (IOException e) {
e.printStackTrace();
}
}
public UserCredentials getCredentials() { return this.credentials; }
}
As you see, I read the data every minute and my question is:
Can I delay the return value of getCredentials, so when the method is called I check if isReading is true and then delay the return so I can guarantee that a caller will always get the actual state of the yaml file?
I think there are appropriate locks for similar situations, but this seems like synchronize is sufficient.
synchronized private void parseCredentials() {...}
synchronized public UserCredentials getCredentials() { ... }
By declaring those methods synchronized only one thread at a time will be able to enter the method, essentially a barrier. That means that parseCredentials could have to wait for getCredentials, but getCredentials is so trivially fast you'll never notice.
That will synchronize on an instance of CredentialReader, so if you use more than one, you might want to synchronize on something else. As mentioned it the comments it is better to synchronize on a private object rather than the instance itself. It is a small change:
public class CredentialsReader {
private UserCredentials credentials;
private boolean isReading = false;
final private Object lock = new Object();
...
Then remove the synchronize from the method signature and add a synchronize call in the body.
private void parseCredentials() {
synchronize(lock){
//original code goes here.
}
}
Also, isReading should be volatile.
I do not suggest to do it manually, you could use a CountDownLatch with init value 1 provided in jdk.
You can let the readers calls await, and let the writer calls countDown once data is prepared.
So the reader could always get fully initialized data.
how do you think, do we need to use synchronized block for better optimization of access to instance of Ad?
The instance of Ad.class can be retrieved from different threads. Synchronized helps to get an instance in one time with one get operation from ConcurrentHashMap. ConcurrentHashMap store all values as volatile. I use it on java 1.7 for android, computeIfAbsent is available in java 1.8.
It will be great to get detailed answer, why not or why yes.
Thank you!
public final class Ad {
private final static Map<String, Ad> ads = new ConcurrentHashMap<>();
public static Ad get(#NonNull String appId) {
if (appId == null) appId = "";
boolean containsAd = ads.containsKey(appId);
Ad localInstance = containsAd ? ads.get(appId) : null;
if (localInstance == null) {
synchronized (Ad.class) {
containsAd = ads.containsKey(appId);
localInstance = containsAd ? ads.get(appId) : null;
if (localInstance == null) {
localInstance = new Ad();
localInstance.setAdId(appId);
ads.put(appId, localInstance);
}
}
}
return localInstance;
}
private Ad() {
}
}
UPDATE: Thanks to all for help. I replaced ConcurrentHashMap to HashMap.
This is not quite optimal. If multiple threads try initialize values at the same time, then they will block each other, even if they are looking for different keys.
You should use ConcurrentHashMap.computeIfAbsent to check for the add and create missing ones in a single step. That way you will not create any Ads that aren't used, and two threads will only block each other if they're trying to initialize the same entry:
public static Ad get(#NonNull String appId) {
if (appId == null) appId = "";
return ads.computeIfAbsent(appId, Ad::new);
}
private Ad(String appId) {
this();
setAdId(appId);
}
From what I understand what you actually want to achieve is putIfAbsent and as such this is much simpler then what you do (your are using a double check locking):
public static Ad get(String appId) {
String newId = appId == null ? "" : appId;
ads.putIfAbsent(newId, new Ad());
return map.get(newId);
}
I have the following set of classes (along with a failing unit test):
Sprocket:
public class Sprocket {
private int serialNumber;
public Sprocket(int serialNumber) {
this.serialNumber = serialNumber;
}
#Override
public String toString() {
return "sprocket number " + serialNumber;
}
}
SlowSprocketFactory:
public class SlowSprocketFactory {
private final AtomicInteger maxSerialNumber = new AtomicInteger();
public Sprocket createSprocket() {
// clang, click, whistle, pop and other expensive onomatopoeic operations
int serialNumber = maxSerialNumber.incrementAndGet();
return new Sprocket(serialNumber);
}
public int getMaxSerialNumber() {
return maxSerialNumber.get();
}
}
SprocketCache:
public class SprocketCache {
private SlowSprocketFactory sprocketFactory;
private Sprocket sprocket;
public SprocketCache(SlowSprocketFactory sprocketFactory) {
this.sprocketFactory = sprocketFactory;
}
public Sprocket get(Object key) {
if (sprocket == null) {
sprocket = sprocketFactory.createSprocket();
}
return sprocket;
}
}
TestSprocketCache unit test:
public class TestSprocketCache {
private SlowSprocketFactory sprocketFactory = new SlowSprocketFactory();
#Test
public void testCacheReturnsASprocket() {
SprocketCache cache = new SprocketCache(sprocketFactory);
Sprocket sprocket = cache.get("key");
assertNotNull(sprocket);
}
#Test
public void testCacheReturnsSameObjectForSameKey() {
SprocketCache cache = new SprocketCache(sprocketFactory);
Sprocket sprocket1 = cache.get("key");
Sprocket sprocket2 = cache.get("key");
assertEquals("cache should return the same object for the same key", sprocket1, sprocket2);
assertEquals("factory's create method should be called once only", 1, sprocketFactory.getMaxSerialNumber());
}
}
The TestSprocketCache unit test always returns a green bar even if I change the following as follows:
Sprocket sprocket1 = cache.get("key");
Sprocket sprocket2 = cache.get("pizza");
Am guessing that I have to use a HashMap.contains(key) inside SprocketCache.get() method but can't seem to figure the logic.
The problem you're having here is that your get(Object) implementation only allows one instance to be created:
public Sprocket get(Object key) {
// Creates object if it doesn't exist yet
if (sprocket == null) {
sprocket = sprocketFactory.createSprocket();
}
return sprocket;
}
This is a typical lazy-loading instantiation singleton pattern. If you invoke get again, an instance will be assigned to sprocket and it will skip the instantiation completely. Note that you don't even use the key parameter at all, so it does not affect anything.
Using a Map would indeed be one way to achieve your objective:
public class SprocketCache {
private SlowSprocketFactory sprocketFactory;
private Map<Object, Sprocket> instances = new HashMap<Object, Sprocket>();
public SprocketCache(SlowSprocketFactory sprocketFactory) {
this.sprocketFactory = sprocketFactory;
}
public Sprocket get(Object key) {
if (!instances.containsKey(key)) {
instances.put(sprocket);
}
return instances.get(key);
}
}
Well, your current Cache implementation does not rely on key, so no wonder it always returns same cached-once value.
If you want to store different values for keys, and assuming you want it to be thread safe, you might end up doing something like this:
public class SprocketCache {
private SlowSprocketFactory sprocketFactory;
private ConcurrentHashMap<Object, Sprocket> cache = new ConcurrentHashMap<?>();
public SprocketCache(SlowSprocketFactory sprocketFactory) {
this.sprocketFactory = sprocketFactory;
}
public Sprocket get(Object key) {
if (!cache.contains(key)) {
// we only wan't acquire lock for cache seed operation rather than for every get
synchronized (key){
// kind of double check locking to make sure no other thread has populated cache while we were waiting for monitor to be released
if (!cache.contains(key)){
cache.putIfAbsent(key, sprocketFactory.createSprocket());
}
}
}
return cache.get(key);
}
}
Couple important side notes:
you'll need CocncurrentHashMap to ensure happens-before paradigm and so other thread will instantly see if cache has been filled;
new cache value creation has to be synchronized so each concurrent
thread won't generate it's own value, overriding previous values during race condition;
synchronization is quite expensive so we only wan't to engage it when needed, and due to same race condition you might get several threads holding monitor at the same time. That is why another check is required AFTER synchronized block to make sure that other thread hasn't already filled that value.
Below is my factory code which starts the background thread TempScheduler -
public class TempClientFactory {
public static IClient getInstance() {
new TempScheduler().startScheduler();
return ClientHolder.INSTANCE;
}
private static class ClientHolder {
private static final TempClient INSTANCE = new TempClient();
}
}
Now customer will call our code using the above factory like this. They will get our Client instance only once and then use that instance going forward to make a call to a read method in my implementation -
IClient client = TempClientFactory.getInstance();
String response = client.read(userId);
And below is my background thread code which will get the data from the URL, parse it and store it in a class variable -
public class TempScheduler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startScheduler() {
final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
callServers();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}, 0, 10, TimeUnit.MINUTES);
}
}
// call the servers and get the data and then parse
// the response.
private void callServers() {
String url = "url";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
parseResponse(response);
}
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
ConcurrentHashMap<String, Map<Integer, String>> primaryTables = null;
//...
// store the data in ClientData class variables which can be
// used by other threads
ClientData.setPrimaryMapping(primaryTables);
}
}
After parsing the data coming from the URL, my above background thread will store the result in my class ClientData in its variables by using its setters. Below is my ClientData class.
public class ClientData {
private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
public static Map<String, Map<Integer, String>> getPrimaryMapping() {
return primaryMapping.get();
}
public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
primaryMapping.set(map);
}
}
Problem Statement:-
Now the interesting part comes, As you can see customer will call our code using the above factory and as soon as they will make a call to getInstance() method, my background thread gets started up which will fetch the data from the URL and then parse it and store it in a class variable. But after the getInstance() method is executed they will make a call to read method in my Client code instantly..
And then I am supposed to use the variable which was set by my background thread in my implementation code. My implementation code which has read method uses CallableTaks and future.get so it will automatically comes to call method below. In the below class I am supposed to use the variable value which was set by my background thread. I need to use getPrimaryMapping method to get the value in my below code.
public class ClientTask implements Callable<String> {
private String userId = null;
public ClientTask(String userId) {
this.userId = userId;
}
#Override
public String call() throws Exception {
//.....
String hostname = ClientData.getPrimaryMapping("some_string").get(some_number);
//....
}
}
The problem with this approach is, as soon the customer calls our factory for the first time to get the instance, then it will start the background thread which will fetch the data from the URL and parse it and store the data in a class variable but it will call the read method of my implementation class instantly..
Now it might be possible that for the first time, my background thread is still parsing the data but the actual call has come inside the call method and then it will try to get the data from getPrimaryMapping method but it won't have anything, right? Why bcoz it is still parsing the data.. So how do I make sure that for the first time whenever customer is calling our code, once the parsing is done, then only allow to get the variable value in the call method.
And then the second time will be fine.. As the data will be in memory, only the first time is the problem..
Is there any way to do this?
It's not an elegant solution, but a CountDownLatch could do the trick:
public class ClientData {
private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
private static final CountDownLatch firstSet = new CountDownLatch(1);
public static Map<String, Map<Integer, String>> getPrimaryMapping() {
try { firstSet.await(); } catch (Exception ignored) {}
return primaryMapping.get();
}
public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
primaryMapping.set(map);
firstSet.countDown();
}
}
I need a way to allow only one thread to modify data related to a service ticket. More than one thread may be attempting to modify the ticket data at the same time.
Below is a simplified version of my approach. Is there a better way to do this? Maybe with java.util.concurrent packages?
public class SomeClass1
{
static final HashMap<Integer, Object> ticketLockMap = new HashMap<Integer, Object>();
public void process(int ticketNumber)
{
synchronized (getTicketLock(ticketNumber))
{
// only one thread may modify ticket data here
// ... ticket modifications here...
}
}
protected static Object getTicketLock(int ticketNumber)
{
Object ticketLock;
// allow only one thread to use map
synchronized (ticketLockMap)
{
ticketLock = ticketLockMap.get(ticketNumber);
if (ticketLock == null)
{
// first time ticket is locked
ticketLock = new Object();
ticketLockMap.put(ticketNumber, ticketLock);
}
}
return ticketLock;
}
}
Additionally, if I don't want the HashMap filling up with unused locks, I would need a more complex approach like the following:
public class SomeClass2
{
static final HashMap<Integer, Lock> ticketLockMap = new HashMap<Integer, Lock>();
public void process(int ticketNumber)
{
synchronized (getTicketLock(ticketNumber))
{
// only one thread may modify ticket data here
// ... ticket modifications here...
// after all modifications, release lock
releaseTicketLock(ticketNumber);
}
}
protected static Lock getTicketLock(int ticketNumber)
{
Lock ticketLock;
// allow only one thread to use map
synchronized (ticketLockMap)
{
ticketLock = ticketLockMap.get(ticketNumber);
if (ticketLock == null)
{
// first time ticket is locked
ticketLock = new Lock();
ticketLockMap.put(ticketNumber, ticketLock);
}
}
return ticketLock;
}
protected static void releaseTicketLock(int ticketNumber)
{
// allow only one thread to use map
synchronized (ticketLockMap)
{
Lock ticketLock = ticketLockMap.get(ticketNumber);
if (ticketLock != null && --ticketLock.inUseCount == 0)
{
// lock no longer in use
ticketLockMap.remove(ticketLock);
}
}
}
}
class Lock
{
// constructor/getters/setters omitted for brevity
int inUseCount = 1;
}
You might be looking for the Lock interface. The second case could be solved by a ReentrantLock, which counts the number of times it has been locked.
Locks have a .lock() method which waits for the lock to acquire and an .unlock method which should be called like
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
This could then be combined with a HashMap<Integer, Lock>. You could omit the synchronized calls and cut down on lines of code.