I know how to make a collection unmodifiable in java but I dont understand the need for such a method to exist. Can someone please explain this with an example scenario where I would want to make my collection unmodifiable?
Thanks in advance
The most efficient way to share private data outside of a class is to simply return it. But then something outside of the class can change the data that the class depends on. Another option is to copy the data before you share. This takes time and memory to do. Unmodifiable collections will often wrap the data and simply present it without allowing an outside class to edit it. This is faster than making a copy. An outside class can optionally make a modifiable copy if it needs.
An unmodifiable collection is basically read-only which is exactly what you want in case you need to publish such collection to client code and you don't want the client code to modify the collection.
It also promotes immutability which is generally a good thing since you won't have to care about the state of the collection for the rest of the execution of your program. See item 15 of Effective Java (2nd Edition) : Minimize mutability, and to quote Joshua Bloch :
Immutable objects are simple. An immutable object can be in exactly
one state, the state in which it was created.
Note that an unmodifiable collection will not make the contained objects immutable. This is a property each of the contained objects needs to make sure of, if it is required of course.
Take a look at this scenario. There is an application that creates 2 users, and then wants to notify them about something. But only users with name different from Peter should get the notification.
So we have to User.class:
public class User {
private String name;
private Integer id;
public User(final Integer id, final String name) {
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
public Integer getId() {
return id;
}
}
The users are stored in special holder class (containing map):
public class UsersHolder {
private static Map<Integer, User> usersMap = new HashMap<Integer, User>();
public static void addUser(final User user) {
usersMap.put(user.getId(), user);
}
public static Map<Integer, User> getUsersMap() {
return usersMap;
//return Collections.unmodifiableMap(usersMap);
}
}
Then we have the UsersCreator that creates those users and stores them in a map:
public class UsersCreator {
public static void createUsers() {
UsersHolder.addUser(new User(1, "Peter"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(1).getName());
UsersHolder.addUser(new User(2, "Paul"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(2).getName());
}
public static void main(String[] args) {
UsersCreator.createUsers();
System.out.println("Number of users before notification: " + UsersHolder.getUsersMap().size());
new UsersNotificator().notifyAllUsersButPeters(UsersHolder.getUsersMap());
System.out.println("Number of users after notification: " + UsersHolder.getUsersMap().size());
}
}
And the notificator that notifies all but Peters:
public class UsersNotificator {
public void notifyAllUsersButPeters(final Map<Integer, User> map) {
//we don't need peters, so we'll remove them from the list;
Iterator<Entry<Integer, User>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue().getName().equals("Peter")) {
iterator.remove();
}
}
//now we can notify all from the list;
notifyUsers(UsersHolder.getUsersMap());
}
private void notifyUsers(Map<Integer, User> map) {
for (final User user : map.values())
System.out.println("notifyingUsers: " + user.getName());
}
}
Now - the notificator was presented with a map and it may modify it, which it does. It doesn't know that it shouldn't modify it as it's global usersMap. In effect it removes all users with name Peter. It does it for it's own purposes, but the results will be visible for every other class using UsersHolder.
The result is as follows:
Created user Peter
Created user Paul
Number of users before notification: 2
notifyingUsers: Paul
Number of users after notification: 1
When returning unmodifiableMap in UsersHolder the removal will not be possible. The only way would be to create new map with users to notify, so our usersHolder is safe.
This example is a bit big, sorry for that, i failed to think of/create somehting shorter.
Unmodifiable map helps to keep your classes Immutable which is safe(as presented in the example) especially in multithreaded enviroment.
There are many situations in which you do not want your collection to be modifiable. Whenever you know that the collection is initialized with exactly the content it should contain at all times, it can provide security to make it unmodifiable.
The (rather long) example provided by another user is a good example of where it often causes problems. Whenever you traverse a collection, there is a risk you change the collection if you forget to do it on a copy. Making the collection unmodifiable catches and prevents this easy to make mistake.
Related
I have a class that creates index cards, and within it, I have an instance variable that is a static HashMap that stores all the instances created.
I have been thinking a lot about it and I thought that the methods that handle the opperations over that HashMap should go in a different class, because those methods don't opperate directly over any index card, they opperate over the list of index cards.
This way, I would have an IndexCard class, and an ListAdministrator class. And both classes would handle different functions.
The problem is that this new class (ListAdministrator) would only have static methods, because there is only one list and there is no reason to create any new list of index cards, I only need one.
Should I move those methods to another class or should I keep it like this? Is that a good practice?
This is the code:
class IndexCard {
public static HashMap <String, IndexCard> list = new HashMap <> ();
public String name;
public String address;
public String phone;
public String email;
public LocalDate dateRegister;
IndexCard(String name, String dni, String address, String phone, String email) {
this.name = name;
this.address = address;
this.phone = phone;
this.email = email;
dateRegister = LocalDate.now();
if (Utils.validarDni(dni) && !list.containsKey(dni)) {
list.put(dni, this);
} else {
throw new InvalidParameterException ("Error when entering the data or the DNI has already been previously registered");
}
}
/**
* Update the data of the selected card.
*/
public void update() throws IllegalAccessException {
String key = getKeyWithObject(this);
Scanner reader = new Scanner(System.in);
Field[] fields = this.getClass().getFields();
for (Field field: fields) {
String nameField = Utils.splitCamelCase(field.getName());
if (!Modifier.isStatic(field.getModifiers()) && (field.getType()).equals(String.class)) {
System.out.println ("Enter new " + nameField);
String value = reader.nextLine().trim();
field.set(this, value);
}
}
reader.close();
list.put(key, this);
System.out.println("Updated data \n \n");
}
/**
* Delete the selected card.
*/
public void delete() throws IllegalAccessException {
String key = getKeyWithObject(this);
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
field.set(this, null);
}
}
list.remove(key);
}
/**
* Displays the data of the selected card on screen.
*/
public void print() throws IllegalAccessException {
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
String nameFieldConSpaces = Utils.splitCamelCase(field.getName());
Object value = field.get(this);
System.out.println(nameFieldConSpaces + ":" + value);
}
}
}
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
All those static methods are what I would put in the new class.
This is how the new class ListAdministrator would look. It would not even need a constructor.
class ListAdministrator{
public static HashMap <String, IndexCard> list = new HashMap <> ();
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
You should keep the concerns of managing the IndexCards and the IndexCards themselves separate because of the Single Responsibility Principle. Furthermore the ListAdministrator should handle everything that deals with the management of the IndexCards, also deletion and creation of the managed objects.
The name ListAdministrator is somehow not meeting the point as it does not administrate lists, maybe use something like IndexCardRegistry.
To deal with concurrency you could use a ConcurrentMap as your main data storage.
Having ListAdministrator all static might come in handy if your IndexCards need access to it or other IndexCards, but this would not be the best design. Do they need to know anyway? From my understanding the IndexCards could be simple POJOs that contain only data and no logic at all.
On the other hand with an all-static ListAdministrator you will not be able to use two instances of managed objects at the same time in the future without major refactoring your code. Even if you never would expect this today a well defined object registry that can handle any object might come in handy in projects to come. Therefore I would rather use real instances for the ListAdministrator (and program against it's interface to stay flexible).
In more detail referring to your comments:
The idea of this approach is to keep concerns clearly separated, which will make future changes to your code feasible in case the project grows (most projects tend to do so). My understanding is that the ListAdministrator should manage your IndexCards. In a way this is the same as Object Relational Mappers work, but at the moment your database is a HashMap. If you create an interface for ListAdministrator you may even swap out the HashMap with a database without having to change its clients.
On second investigation of your code I found that IndexCards not only store the data but as well have methods to update the data. This represents another break of the Single Responsibility Principle and should be dealt with. If the ListAdministrator would provide an update method for a given IndexCard it could be used by as many different clients you can think of without changing any code behind the ListAdministrators API. Your first client would be the command-line interface you already have programmed, the next might be a web service.
With an all-static ListAdministrator you have one static Class that manages one static data set. It will always only deal with IndexCards, everything you add will end up in the same HashMap (if allowed/compatible). Every part of your application with access to the class ListAdministrator would have full access to the data. If you needed another ListAdministrator (handling create, delete, update, search) for a different type you would have to refactor everything to accomodate this or start duplicating code. Why not create an instance based solution in the first place. You would have your repository for IndexCards, and could add new repositories at will.
Maybe this is over-engineering for your use case but in keeping the responsibilities clearly separated you will find out that many extensions of your code will happen orthogonal (not affecting existing code), and this is where the fun really begins. And how do you want to practice this if not with smaller projects.
More details about the reason of using interfaces for flexible code (in response to latest comment)
The short answer is: always code against an interface (as stated in numerous articles and java books). But why?
A Java interface is like a contract between a class and its clients. It defines some methods, but does not implement them itself. To implement an interface you define a class with class XYZ implements SomeInterface and the source code of the class does whatever it finds reasonable to answer to the methods defined in the interface. You try to keep the interface small, to contain only the essential methods because the smaller the interface is, the less methods you have to take into account when changes have to be made.
A common idiom in Java would be to define a List<T> return type (the interface) for a method, which most likely would be an ArrayList (concrete class), but could be a LinkedList (another concrete class) as well, or anything else that implements the List interface. By just returning the List interface you prevent your client to use other methods of the otherwise returned concrete class as well which would greatly reduce your freedom to change the internal implementation of your "ListProvider". You hide the internal implementation but agree to return something that fulfills the given interface. If you want to conceed to even less obligations, you could return the interface Iteratable instead of List.
Checkout the Java API, you will find standard classes like ArrayList implement many interfaces. You could always use an ArrayList internally and return it as the smallest interface possible to do the job.
Back to your project. It would be essential to refer to the Registry (ListAdministrator) via its interface, not its concrete class. The interface would define methods like
interface IndexCardRegistry {
void delete(Long id) throws IllegalAccessException;
void update(Long id, Some data) throws IllegalAccessException;
// ...
}
What it does is of no concern for the client, it just hopes everything goes right. So if a client calls the repositories update method it would rely on the repository to update the targeted IndexCard. The repository could store the data as it wants, in a HashMap, in a List or even in a database, it would not matter to the clients.
class IndexCardMapBasedRegistry implements IndexCardRegistry {
private Map store = new HashMap();
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the hashmap
}
void update(Long id, Some data) throws IllegalAccessException {
// code to get the IndexCard with id from
// the hashmap and update its contents
}
// ...
}
Now the new iteration, at creation of your registry you swap out IndexCardMapBasedRegistry for the new
class IndexCardDatabaseRegistry implements IndexCardRegistry {
private Database db;
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the database
}
void update(Long id, Some data) throws IllegalAccessException {
// code to update the IndexCard with id in the database
}
// ...
}
IndexCardRegistry indexCards = new IndexCardMapBasedRegistry(); becomes
IndexCardRegistry indexCards = new IndexCardDatabaseRegistry();
The client must not change at all, but the Registry would be able to handle an amount of IndexCards that otherwise would blow your computers memory.
Stay with IndexCard class and dont need to create new class ListAdministrator
In class IndexCard you have list as of type hashmap and it represent in memory data structure and you have n number of method in this class to work in this data structure so i suggest stay with single class as it will serve single responsibility.
I am working on measuing my application metrics using below class in which I increment and decrement metrics.
public class AppMetrics {
private final AtomicLongMap<String> metricCounter = AtomicLongMap.create();
private static class Holder {
private static final AppMetrics INSTANCE = new AppMetrics();
}
public static AppMetrics getInstance() {
return Holder.INSTANCE;
}
private AppMetrics() {}
public void increment(String name) {
metricCounter.getAndIncrement(name);
}
public AtomicLongMap<String> getMetricCounter() {
return metricCounter;
}
}
I am calling increment method of AppMetrics class from multithreaded code to increment the metrics by passing the metric name.
Problem Statement:
Now I want to have metricCounter for each clientId which is a String. That means we can also get same clientId multiple times and sometimes it will be a new clientId, so somehow then I need to extract the metricCounter map for that clientId and increment metrics on that particular map (which is what I am not sure how to do that).
What is the right way to do that keeping in mind it has to be thread safe and have to perform atomic operations. I was thinking to make a map like that instead:
private final Map<String, AtomicLongMap<String>> clientIdMetricCounterHolder = Maps.newConcurrentMap();
Is this the right way? If yes then how can I populate this map by passing clientId as it's key and it's value will be the counter map for each metric.
I am on Java 7.
If you use a map then you'll need to synchronize on the creation of new AtomicLongMap instances. I would recommend using a LoadingCache instead. You might not end up using any of the actual "caching" features but the "loading" feature is extremely helpful as it will synchronizing creation of AtomicLongMap instances for you. e.g.:
LoadingCache<String, AtomicLongMap<String>> clientIdMetricCounterCache =
CacheBuilder.newBuilder().build(new CacheLoader<String, AtomicLongMap<String>>() {
#Override
public AtomicLongMap<String> load(String key) throws Exception {
return AtomicLongMap.create();
}
});
Now you can safely start update metric counts for any client without worrying about whether the client is new or not. e.g.
clientIdMetricCounterCache.get(clientId).incrementAndGet(metricName);
A Map<String, Map<String, T>> is just a Map<Pair<String, String>, T> in disguise. Create a MultiKey class:
class MultiKey {
public String clientId;
public String name;
// be sure to add hashCode and equals
}
Then just use an AtomicLongMap<MultiKey>.
Edited:
Provided the set of metrics is well defined, it wouldn't be too hard to use this data structure to view metrics for one client:
Set<String> possibleMetrics = // all the possible values for "name"
Map<String, Long> getMetricsForClient(String client) {
return Maps.asMap(possibleMetrics, m -> metrics.get(new MultiKey(client, m));
}
The returned map will be a live unmodifiable view. It might be a bit more verbose if you're using an older Java version, but it's still possible.
I am working on on a program. I was storing Users in a ArrayList, so I had a UserCollection class which is acting as the storage class for the Users. But seeing as the UserCollection is thought of as a 'database' each user entry in the database should have a unique id. Initially I had a userID as a field in the User class but now I'm trying to handle the id part in the UserCollection. If I were to use a hashmap, where the key would be the id, the value being the User how would I go on about iterating the id so that every time a new User is stored into the hashmap, they key keeps iterrating from 1 to n amount of users.I'm also using CRUD methods to store/remove/update etc the Users.
public class UserCollection{
Map<Integer, User> userMap = new HashMap<Integer,User>();
public User create(User user){
userMap.put(??,user) // not sure how to iterate the id or what to put in it
return user;
}
public User read(Integer keyID){
if(userMap.containsKey(keyID)){
return userMap.get(keyID); //something definitely wrong
}
}
//Other remaining CRUD methods after.
}
Originally I just had an ArrayList which held Users. But Because I realized in a database Users will have unique id's now I'm confused how I would handle them. If i handle them in the HashMap do I still need to have a userID field in the User class ?
You have asked a couple of questions here. I'll take each of them in turn:
How can I ensure that each user has a unique ID?
The simplest way to do this is to have a static field that keeps track of the largest generated id:
class User {
private static int largestID = 0;
private final int id = largestID++;
public int getID() {
return id;
}
...
}
This works but has plenty of problems with it. As soon as you store users & restart the programme, or want to reuse ids, it needs changing.
Another approach is to just find the largest id for existing customers from your collection. The following sample code uses Java 8:
class UserCollection {
private final Map<Integer,User> users = new HashMap<>();
public int nextID() {
return users.keySet().stream()
.mapToInt(n -> n).max().orElse(-1) + 1;
}
}
This is inefficient but probably good enough for a lot of applications.
Do I need to store the ID in the User class?
You have two options. Either you store it in the class and provide a mechanism for getting the ID (as above). Or you ensure that everything that deals with users stores and returns IDs, not references to objects. This allows the client of the method to then access the User using the ID.
Both of these are legitimate solutions. If you use the first (storing the ID in the class) then you should change your method for adding a User to your collection:
class UserCollection {
public void addUser(User user) {
users.put(user.getID(), user);
}
}
If you use the second, the ID field must be final because clients are relying on it not changing.
You could do the following to ensure that your Users have a unique ID for the life of the class.
public class UserCollection
{
private static int id = 0;
private static final Object lock = new Object();
private Map<Integer, User> userMap = new HashMap<Integer,User>();
public User create (User user)
{
// only add one user at a time to the map
synchronized (lock)
{
id++;
userMap.put(id, user);
}
return user;
}
// Rest of the class
}
I have a class MyList,in Java, with some basics operations like add,get and remove.Also in my list I must have a record with operations which have done with the list.How I can do this record?
You have to define a new class atribute. It can be a String. Like this:
public MyList{
private String record;
....
}
Then on each of your methods you update this atribute:
record += "\n"+*the operation*;
But if you want a better performance, you should use a StringBuffer instead of a String (see here).
Was this the answer that you wanted?
if I understand you want to keep track of updates made to MyList in order to have an history of what has been done.
Why not creating a ListHistory class
public class ListHistory {
private Action action;
private Object data;
public ListHistory(Action action, Object data) {
this.action = action;
this.data = data;
}
}
and the Action enum type (so you can make it evolve)
public enum Action {
get, add, remove;
}
and define a private List<ListHistory> history; variable to keep track of updates ?
You then have the ability to manipulate the history list as you want, add date to ListHistory if needed (or use a Map<Date, ListHistory>)
Hope this will help.
regards,
Yann
I am trying to implement distributed cache using Hazelcast in my application. I am using Hazelcast’s IMap. The problem I have is every time I get a value from a map and update the value, I need to do a put(key, value) again. If my value object has 10 properties and I have to update all 10, then I have to call put(key, value) 10 times. Something like -
IMap<Integer, Employee> mapEmployees = hz.getMap("employees");
Employee emp1 = mapEmployees.get(100);
emp1.setAge(30);
mapEmployees.put(100, emp1);
emp1.setSex(“F”);
mapEmployees.put(100, emp1);
emp1.setSalary(5000);
mapEmployees.put(100, emp1);
If I don’t do this way, some other node which operates on the same Employee object will update it and the final result is that the employee object is not synchronized. Is there any solution to avoid calling put explicitly multiple times? In a ConcurrentHashMap, I don’t need to do this because if I change the object, the map also gets updated.
As of version 3.3 you'll want to use an EntryProcessor:
What you really want to do here is build an EntryProcessor<Integer, Employee> and call it using
mapEmployees.executeOnKey( 100, new EmployeeUpdateEntryProcessor(
new ObjectContainingUpdatedFields( 30, "F", 5000 )
);
This way, Hazelcast handles locking the map on the key for that Employee object and allows you to run whatever code is in the EntryProcessor's process() method atomically including updating values in the map.
So you'd implement EntryProcessor with a custom constructor that takes an object that contains all of the properties you want to update, then in process() you construct the final Employee object that will end up in the map and do an entry.setValue(). Don't forget to create a new StreamSerializer for the EmployeeUpdateEntryProcessor that can serialize Employee objects so that you don't get stuck with java.io serialization.
Source:
http://docs.hazelcast.org/docs/3.5/manual/html/entryprocessor.html
Probably a transaction is what you need. Or you may want to take a look at distributed lock.
Note that in your solution if this code is ran by two threads changes made by one of them will be overwriten.
This may interest you.
You could do something like this for your Employee class (simplified code with one instance variable only):
public final class Employee
implements Frozen<Builder>
{
private final int salary;
private Employee(Builder builder)
{
salary = builder.salary;
}
public static Builder newBuilder()
{
return new Builder();
}
#Override
public Builder thaw()
{
return new Builder(this);
}
public static final class Builder
implements Thawed<Employee>
{
private int salary;
private Builder()
{
}
private Builder(Employee employee)
{
salary = employee.salary;
}
public Builder withSalary(int salary)
{
this.salary = salary;
return this;
}
#Override
public Employee freeze()
{
return new Employee(this);
}
}
}
This way, to modify your cache, you would:
Employee victim = map.get(100);
map.put(100, victim.thaw().withSalary(whatever).freeze());
This is a completely atomic operation.
If there is possibility that another node can update data that your node is working with then using put() will overwrite changes made by another node. Usually it is unwanted behavior, cause it leads to data loss and inconsistent data state.
Take a look at IMap.replace() method and other ConcurrentMap related methods. If replace() is failed then you've faced changes collision. In this case you should give it another attempt:
re-read entry from hazelcast
update it's fields
save to hazelcast with replace
After some failed attempts you can throw StorageException to the upper level.
You should use tryLock on your map entry :
long timeout = 60; // Define your own timeout
if (mapEmployees.tryLock(100, timeout, TimeUnits.SECONDS)){
try {
Employee emp1 = mapEmployees.get(100);
emp1.setAge(30);
emp1.setSex(“F”);
emp1.setSalary(5000);
mapEmployees.put(100, emp1);
} finally {
mapEmployees.unlock(100);
}
}else{
// do something else like log.warn(...)
}
See : https://docs.hazelcast.com/imdg/4.2/data-structures/fencedlock#releasing-locks-with-trylock-timeout