In Java how to synchronize cache read and write operations - java

I am working on implementing a simple cache using ArrayList in my application.
I would like to synchronize cache update operations, while updating the cache I should not allow to perform read operations. So once cache update is completed, then only cache should allow to read.
ContextManager.java
public class ContextManager{
private List<String> trashCanIds;
public List<String> getIds() {
return ids;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
}
ConfigManager.java
public class ConfigManager{
ContextManager ctxManager = new ContextManager();
public synchronized List<String> loadIds() throws Exception {
Utils utils = new Utils();
List<String> listIds = null;
String[] ids = utils.fetchIds();
if(Objects.nonNull(ids) && ids.length > 0) {
listIds = new ArrayList<>(Arrays.asList(ids[0].split(",")));
}
ctxManager.setIds(idsList);
return idsList;
}
}
DeleteManager.java
public class DeleteManager {
ConfigManager configManager = new ConfigManager();
configManager.loadIds();
}
TestManager.java
public class TestManager {
ContextManager contextManager = new ContextManager();
contextManager.getIds();
}
In this code I have synchronized the loadIds() method.
Need help, how to prevent reading getIds() while loadIds() in progress.

You could achieve this by using the ReadWriteLock interface implemented with a ReentrantReadWriteLock instance. This class can represent your case of read and write by acquiring the corresponding lock when performing the getIds and loadIds operations. In fact,
A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html
Basically, your loadIds should acquire the write-lock before proceeding with its operations. If it succeeds, it immediately acquires the lock and carries on with its computation; otherwise it blocks the corresponding thread until the lock is obtained or an InterruptedException is thrown.
On the other hand, the getIds method should acquire the read-lock instead. Where the current thread immediately obtains the lock if this is available; otherwise it blocks the corresponding thread until the lock is obtained or an InterruptedException is thrown.
ContextManager.java
public class ContextManager{
private List<String> trashCanIds;
private ReadWriteLock lock;
private Lock readLock;
private Lock writeLock;
public ContextManager(){
lock = new ReentrantReadWriteLock(true);
readLock = lock.readLock();
writeLock = lock.writeLock();
}
public List<String> getIds() {
readLock.lock();
try {
List<String> tempTrashCanIds = new ArrayList(trashCanIds);
} finally {
readLock.unlock();
}
return tempTrashCanIds;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
public void readLock(){
this.readLock.lock();
}
public void readUnlock(){
this.readLock.unlock();
}
public void writeLock(){
this.writeLock.lock();
}
public void writeUnlock(){
this.writeLock.unlock();
}
}
ConfigManager.java
public class ConfigManager{
ContextManager ctxManager = new ContextManager();
public List<String> loadIds() throws Exception {
Utils utils = new Utils();
List<String> listIds = null;
String[] ids = utils.fetchIds();
if(Objects.nonNull(ids) && ids.length > 0) {
listIds = new ArrayList<>(Arrays.asList(ids[0].split(",")));
}
ctxManager.writeLock();
try {
ctxManager.setIds(idsList);
} finally {
ctxManager.writeUnlock();
}
return idsList;
}
}

Related

Race condition in Resource ID based locking manager

I'm trying to write a locking manager that will be called from multiple threads. This manager handles locking based on various resource IDs. These can vary very much, so keeping a lock in memory for each one would probably cause large memory usage. This is why after a lock is no longer used (the number of threads using it reaches 0), it is removed from memory.
It can exclusively lock threads based on the requested resource ID (if two threads lock the same ID, one will wait for the other to unlock it), or completely exclude all other threads using a ReentrantReadWriteLock.
I am experiencing a race condition where a lock is removed from memory when unlocked by the last thread that holds it, but other threads still try to unlock it? This results in a NPE which I cannot explain.
I have tried using AtomicInteger instead of the current volatile variable, thinking it may have something to do with that but it had similar results.
Here is the problematic class:
/**
* This class provides locks for reading and writing, and bulk operations lock on the entire class.
*
* If a bulk operation is not in progress, class based locking is transparent.
* #author KiralyCraft
*
*/
public class ReadWriteHighLevelLocking
{
private class Semaphore
{
private ReentrantLock lock;
private volatile int acquiredLocks;
public Semaphore()
{
this.acquiredLocks = 0;
this.lock = new ReentrantLock();
}
public synchronized int incrementAndGet()
{
return ++acquiredLocks;
}
public synchronized int decrementAndGet()
{
return --acquiredLocks;
}
}
private ReentrantReadWriteLock classBasedLock;
private volatile HashMap<String, Semaphore> stateChangeLocks;
public ReadWriteHighLevelLocking()
{
this.stateChangeLocks = new HashMap<String,Semaphore>();
this.classBasedLock = new ReentrantReadWriteLock();
}
/**
* Acquires a lock for the specified resource ID.
*
* May block if another thread is currently holding a bulk lock.
* #param resourceID
*/
public void acquireLock(String resourceID)
{
classBasedLock.readLock().lock(); //Using it reversed. There can be any number of operations (using the read lock), but only one bulk operation (sacrifice)
Semaphore stateChangeLock;
synchronized(stateChangeLocks)
{
if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
{
stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
}
}
stateChangeLock.lock.lock();
stateChangeLock.incrementAndGet();
}
public void releaseLock(String resourceID)
{
Semaphore stateChangeLock;
synchronized(stateChangeLocks)
{
stateChangeLock = stateChangeLocks.get(resourceID);
if (stateChangeLock.decrementAndGet() == 0) //<----------------- HERE IS THE NPE
{
stateChangeLocks.remove(resourceID);
}
}
stateChangeLock.lock.unlock();
classBasedLock.readLock().unlock();
}
/**
* When a bulk lock is acquired, all other operations are delayed until this one is released.
*/
public void acquireBulkLock()
{
classBasedLock.writeLock().lock(); //Using it reversed. There can be any number of writers (using the read lock), but only one reader (sacrifice)
}
public void releaseBulkLock()
{
classBasedLock.writeLock().unlock();
}
}
Sample caller class:
public abstract class AbstractDatabaseLockingController
{
...
private ReadWriteHighLevelLocking highLevelLock;
public AbstractDatabaseLockingController(DatabaseInterface db)
{
this.db = db;
this.highLevelLock = new ReadWriteHighLevelLocking();
}
...
public <T extends DatabaseIdentifiable> boolean executeSynchronizedUpdate(T theEntity,AbstractSynchronousOperation<T> aso)
{
boolean toReturn;
String lockID = theEntity.getId()+theEntity.getClass().getSimpleName();
highLevelLock.acquireLock(lockID);
toReturn = aso.execute(theEntity,db);
highLevelLock.releaseLock(lockID);
return toReturn;
}
...
public <T extends DatabaseIdentifiable> List<T> executeSynchronizedGetAllWhereFetch(Class<T> objectType, DatabaseQuerySupplier<T> dqs)
{
List<T> toReturn;
highLevelLock.acquireBulkLock();
toReturn = db.getAllWhere(objectType, dqs);
highLevelLock.releaseBulkLock();
return toReturn;
}
}
NOTE: All places where such a locking manager is used follow the acquire/release pattern from the sample class. It's basically the only place where it is used. Other threads may call the above methods indirectly through the sample class's children
I seem to have fixed the issue by updating the following code:
synchronized(stateChangeLocks)
{
if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
{
stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
}
}
stateChangeLock.lock.lock();
stateChangeLock.incrementAndGet();
to
synchronized(stateChangeLocks)
{
if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
{
stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
}
stateChangeLock.incrementAndGet();
}
stateChangeLock.lock.lock();

Java delay return value

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.

Can I synchronize method by parameter

Can I synchronize method by parameter?
For example - I get person to some method and I want to do some operation for person, but if few thread call this method for the same person I want to do it one by one.
private void dosomething(Long id, Person person) {
dosomethingelse(id, person);
}
How to call dosomethingelse (id, person) only for the same id one by one? but I want that this code for different id-s can be called multithreadly
I wrote this code, but maybe something wrong here or something can be better.
public static class LatchByValue <T> {
public void latch(T value, ConsumerWithException<T> consummer) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try {
CountDownLatch previousLatch = null;
// we are checking if another thread is already calling this method with the same id
// if sync has CountDownLatch so another thread is already calling this method
// or we put our latch and go on
while ((previousLatch = sync.putIfAbsent(value, latch)) != null) {
try {
// we are waiting for another thread, we are waiting for all threads that put their latch before our thread
previousLatch.await();
} catch (InterruptedException e) {
return;
}
}
consummer.accept(value);
} finally {
latch.countDown();
sync.remove(value, latch);
}
}
private ConcurrentHashMap<T, CountDownLatch> sync = new ConcurrentHashMap<>();
}
Example:
LatchByValue<Long> latch = new LatchByValue<>();
private void dosomething(Long id, Person person) {
latch.latch(
id,
currentId -> { dosomethingelse(currentId, person); }
);
}
Problem with using a CountdownLatch is that you can't "increment" the count so you need to replace the existing latch when it's been used, which complicates the code.
You could instead use a Semaphore with one permit which would allow you to do the same thing but in a simpler way.
Semaphore s = sync.computeIfAbsent(value, x -> new Semaphore(1, true));
s.acquire(); //this blocks and throws InterruptedException, which you need to handle
try {
consummer.accept(value);
} finally {
s.release();
}
You can use synchronized keyword on the parameter passed (culprit: it cannot be null!). And that also allows you to stop worrying about re-acquiring the lock (it's reentrant).
So the implementation would look like:
private void doSomething(Long id, Person person) {
synchronized (person) {
// do something
}
}
Remember that any other accesses (not in doSomething call) also would need to have the synchronization block, e.g.:
// another method, unrelated, but does something with 'person'
private void doSomethingElse(Person person, ... /* other arguments */) {
synchronized (person) {
// do something
}
}
It would be good document (in Person's javadoc) that the user needs to acquire the lock for that object.
If you want to provide a critical section for <id, person> tuple, you'd need to change your API a bit - and then pass that object around in your application.
private void doSomething(IdAndPerson idAndPerson) {
synchronized (idAndPerson) {
// do something
}
}
class IdAndPerson {
private final Long id;
private final Person person;
// constructor etc.
}
private static final Set<Long> lockedIds = new HashSet<>();
private void lock(Long id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Long id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void doSomething(Long id) throws InterruptedException {
try {
lock(id);
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(id);
}
}
id can be not only an 'Long' but any class with correctly overridden 'equals' and 'hashCode' methods.
try-finally - is very important - you must guarantee to unlock waiting threads after your operation even if your operation threw exception.
It will not work if your back-end is distributed across multiple servers/JVMs.

Java Multithread with shared List

I need a little help with java multithread. I have this class:
public class EdgeServer{
private static final int ServidorBordaID = 9;
private static final String urlLogin = "http://localhost/exehdager-teste/index.php/ci_login/logar";
private static final String insertSensorURI = "http://localhost/exehdager-teste/index.php/cadastros/ci_sensor/gravaSensor";
private static final String insertGatewayURI = "http://localhost/exehdager-teste/index.php/cadastros/ci_gateway/gravaGateway";
private static ArrayList<Gateway> gatewaysCadastrados = new ArrayList<>();
public static void main(String[] args) {
// Start a user thread that runs the UPnP stack
Thread clientThread = new Thread(new Descoberta());
clientThread.setDaemon(false);
clientThread.start();
Thread publicationThread = new Thread(new Publication());
publicationThread.setDaemon(false);
publicationThread.start();
}
}
The thread Descoberta will add new itens to gatewaysCadastrados list on demand. and the Publication thread will read this list and execute an action for each object on list.
I just need to know how to share and pass this var to threads. Will I need to build a semaphore to do this?
Here is the sample code where you can share list between two threads and you need to use wait and notify for semaphore.
public class Descoberta extends Thread {
private final ArrayList<Gateway> a = new ArrayList<>();
public Descoberta( ArrayList<Gateway> a) {
this.a = a;
}
#Override
public void run() {
synchronized (a) {
while(true){ // your condition
a.wait();
}
a.notify();
}
}
}
public class Publication extends Thread {
private final ArrayList<Gateway> b = new ArrayList<>();
public Publication(ArrayList<Gateway> b) {
this.b = b;
}
#Override
public void run() {
synchronized (b) {
while(true){ // your condition
b.wait();
}
b.notify();
}
}
}
public class EdgeServer {
public static void main(String args[]) {
private final ArrayList<Gateway> gatewaysCadastrados = new ArrayList<>();
Thread clientThread = new Descoberta(gatewaysCadastrados);
Thread publicationThread = new Publication(gatewaysCadastrados);
clientThread.start();
publicationThread.start();
}
}
A simple way it to pass the shared object as a constructor parameter to the relevant runnables; e.g.
Thread clientThread = new Thread(new Descoberta(gateways));
...
Thread publicationThread = new Thread(new Publication(gateways));
Obviously, the respective Runnable constructors need to save the parameters so that their run() methods can find them.
There are various other ways:
If the runnables are inner classes within the same outer class instance, they can access shared objects in outer class.
If the shared state is stored in static variables, then the runnables can access them via static getters, etcetera. (NOTE: this is most likely bad design ...)
And as #Fidor points out, if two or more threads are going to share a common (mutable) data structure, then they need to synchronize their read and write operations on the data structure. If you neglect this, your application is liable to have the kind of insidious bugs that are hard to reproduce and hard to track down.

Thread synchronization based upon an id

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.

Categories

Resources