I have 2 caches which are updated individually form various parts of my code. Every some time (e.g. 12 hours) I want to make sure they are synced. An external class is responsible for starting and executing this task. How can I make sure other classes are not working with the caches when this happens?
My thinking is using some ReadWriteLock in each cache and exposing lock/unlock methods.
Class Cache {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void put(String id, Object object) {
lock.readLock().lock();
try {
// put in cache
} finally {
lock.readLock().unlock();
}
}
public Object get(String id) {
lock.readLock().lock();
try {
// get from cache
} finally {
lock.readLock().unlock();
}
}
public void lock() {
lock.writeLock().lock();
}
public void unlock() {
lock.writeLock().unlock();
}
}
And this is the code for the sync class
Class Synchronizer {
Cache cache1 = new Cache();
Cache cache2 = new Cache();
public void syncCaches() {
cache1.lock();
cache2.lock();
try {
// do sync
} finally {
cache1.unlock();
cache2.unlock();
}
}
}
This works, but I think it's a misuse of the Read/Write lock architecture and I couldn't find libraries or something else what might work.
Any ideas are welcome!
Related
I am just learning multithreading in Spring Framework and I don't know how to deal with one case. I have a long-lasting operation and I do not want the user to wait for it to be done, I find out that there is an #Async annotation which flag the method as executable asynchronously.
My question is what would be the best way to block this method such that users from the same company cannot perform it on the same time. Being accurate I want to block even performing analyzeData(...) and anlyzeStatistics(...) by users from the same company on the same time.
I was thinking about using ConcurrentHashMap with user company as key and boolean as value and checking it before performing the operation. I wonder if I'm going in the right direction, or maybe there are other more appropriate options offered by Spring.
#Service
public class LongOperationService {
#Async
public void analyzeData(User user, List<String> data) {
boolean operationResult = performLongOperation(data);
if (opeartionResult) {
log.info("Long operation ended successfully")
} else {
log.error("Long operation failure")
}
}
#Async
public void analyzeStatistics(User user, List<String> statistics) {
...
}
private void performLongOperation(List<String> data) {
// Just for demonstration
Thread.sleep(10000);
return true;
}
}
public class User {
String username;
String company;
}
You can use Semaphore to limit number of threads accessing a resource.
Since you want to prevent users from same company to access your analyze function concurrently, you should create semaphore per company:
// Init on startup
// Key should be a unique identifier to a company, I assume the `String company` as key, you should adjust as your real requirement
static final Map<String, Semaphore> COMPANY_ENTRANT = new ConcurrentHashMap<>();
// for each company
COMPANY_ENTRANT.put(companyId, new Semaphore(1));
Now in your service:
#Async
public void analyzeData(User user, List<String> data) {
Semaphore entrant = COMPANY_ENTRANT.get(user.getCompany());
try {
entrant.acquire();
try {
boolean operationResult = performLongOperation(data);
if (opeartionResult) {
log.info("Long operation ended successfully")
} else {
log.error("Long operation failure")
}
} finally {
entrant.release();
}
} catch(InterruptedException e) {
...
}
}
If you want a lazy initialization of the COMPANY_ENTRANT map, you can use putIfAbsent:
Semaphore entrant = COMPANY_ENTRANT.putIfAbsent(user.getCompany(), new Semaphore(1));
Try something like this:
private final Set<String> runningOperations = Collections.synchronizedSet(new HashSet<>());
private final Object lock = new Object();
#Async
public void analyzeData(User user, List<String> data) throws Exception {
synchronized (lock) {
if (runningOperations.contains(user.company))
return;
runningOperations.add(user.company);
}
try {
boolean operationResult = performLongOperation(data);
if (operationResult) {
log.info("Long operation ended successfully");
} else {
log.error("Long operation failure");
}
} finally {
runningOperations.remove(user.company);
}
}
I have two synchronized methods, each of them is being executed by a different Thread.
public synchronized ResultSet dbExecuteQuery(String queryStmt);
public synchronized void dbExecuteUpdate(String queryStmt);
How can I make sure their execution won't "overlap"?
One solution that comes to my mind is the following:
public synchronized ResultSet dbExecute(String queryStmt, boolean isUpdate) {
if (isUpdate) {
dbExecuteUpdate(queryStmt);
return null;
} else
return dbExecuteQuery(queryStmt);
}
But it means I would have to change all code used in the whole project. Is there a cleaner way to do it?
You can add a dedicated synchronization object:
class YourClass {
Object syncObject = new Object();
public ResultSet dbExecuteQuery(String queryStmt) {
synchronized(syncObject) {
// your code
}
}
public void dbExecuteUpdate(String queryStmt) {
synchronized(syncObject) {
// other code
}
}
}
But it's probably better to use a ReentrantLock.
class YourClass {
private Lock lock = new ReentrantLock();
public ResultSet dbExecuteQuery(String queryStmt) {
lock.lock();
// your code
lock.unlock();
}
public void dbExecuteUpdate(String queryStmt) {
lock.lock();
// other code
lock.unlock();
}
}
Actually, since one is a read and one a write, you probably want to use ReadWriteLock.
class YourClass {
private ReadWriteLock lock = new ReentrantReadWriteLock();
public ResultSet dbExecuteQuery(String queryStmt) {
lock.readLock().lock();
// your code
lock.readLock()..unlock();
}
public void dbExecuteUpdate(String queryStmt) {
lock.writeLock()..lock();
// other code
lock.writeLock().unlock();
}
}
This way you can have several threads reading concurrently:
The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.
I'm looking for a solution that allows multiple threads to read the shared resource (concurrency permitted) but then locks these reading threads once a thread enters a mutating block, to achieve best of both world.
class Foo {
Map<String, String> sharedResource;
public void read() // multiple reading threads allowed, concurrency ok, lock this only if a thread enters the mutating block below.
{
// read concurrently unless a thread enters mutating blocks add/remove
}
public void add() // this should lock any threads entering this block as well as lock the reading threads above
{
synchronized(sharedResource) // lock remove and read
{
}
}
public void remove() // lock add and read
{
synchronized(sharedResource)
{
}
}
}
Is there such a solution in Java?
It's a classic read/write lock scenario:
class Foo {
Map<String, String> sharedResource;
ReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock();
try {
// read
} finally {
lock.readLock().unlock();
}
}
public void add() {
lock.writeLock().lock();
try {
// add
} finally {
lock.writeLock().unlock();
}
}
public void remove() {
lock.writeLock().lock();
try {
// remove
} finally {
lock.writeLock().unlock();
}
}
}
The read lock can be shared, but the write lock is exclusive to both reads and writes.
I wonder what would be the most efficient solution to this problem.
I have a multithreaded database implementation (LevelDB, for example) and I want it to handle synchronization, since it can do it better. However, I want to initialize the database asynchronously, without blocking any thread unless they somehow want to use the database before it has been opened.
Something along the lines of:
public class Storage {
Database db;
public Storage() {
open();
}
private void open() {
new Thread(new Runnable() {
public void run() {
// attempt to open db here, i.e. change the value of Storage.db from null
// into Object
}
}).run();
}
public void accessMethod() {
// this method should only use a non-null Storage.db value, it should block
// until the thread above does not set the value of db to be an Object
}
public void nonAccessMethod() {
// this method is not concerned with the value inside Storage.db and should not
// block while the thread above is running
// example: memory cached operations on the db which will be executed after
// the thread above finishes and "unlocks" Storage.db
}
}
I came up with this solution, but it is not very efficient:
public class Storage {
ReentrantLock lock;
Database db;
public Storage() {
lock = new ReentrantLock();
open();
}
private void open() {
lock.lock(); // to be released in thread below
new Thread(new Runnable() {
public void run() {
// heavy work here while populating Storage.db
lock.unlock();
}
}).run();
}
// returns true if the database is not yet open and that we need to release
// the lock once our code segment completes
private boolean blockIfNotOpen() {
if (lock.tryLock()) {
lock.unlock(); // << this code segment sucks
return false;
} else {
lock.lock();
return true;
}
}
public void accessMethod() {
boolean wasNotOpen = blockIfNotOpen();
// "blocking" code here
if (wasNotOpen) {
lock.unlock();
}
}
public void nonAccessMethod() {
// not concerned with Storage.db and therefore not trying to lock
}
}
I don't like this solution because it still synchronizes access to the database in the implementation of Storage long after Storage.db has been populated, when in fact there is an underlying system inside DB which can handle concurrency better (example: DB exposes worker threads, etc.).
Synchronizing on the Storage object is not a solution since it will, well synchronize, all the time, instead of only when Storage.db is null.
Notes: I'm guaranteed that there will be no concurrent calls before Storage's constructor finishes, if you're worried about the lock. :) So, all concurrency happens after the constructor.
I think the solution would be using ReadWriteLock - writeLock().lock() in constructor, writeLock().unlock() after the db is initialized and the readLock() is used from the db consumers.
Another option, use Future:
public class Storage {
private final Future<Database> dbFuture = Executors.newSingleThreadExecutor().submit(
new Callable<Database>()
{
public Database call()
{
return new Database(...);//Long running DB initialisation
}
}
);
public void accessMethod()
{
Database db = dbFuture.get();// will wait while the call() is not completed yet.
}
}
I have a class named "Channel" with two methods defined:
class Channel {
void read(){...}
void write(){...}
}
There's an instance of this class used in multi-threaded environment. Several threads periodically read from channel while one thread periodically writes to it. Read operation is thread-safe so that it is ok for several reads to occur simultaneously. However once write operation starts, read threads must be blocked until write operation is finished. It is essential to keep read operations as fast as possible and avoid resource-consuming synchronization routines.
What would be the most appropriate pattern to implement such behaviour? Maybe java classes or libraries to help?
Use a ReadWriteLock. It will allow concurrent reads to occur with serial writes. To further satisfy your requirements, an acquisition of a writeLock will prevent any readLock's from making progress until a subsequent release.
class Channel {
final ReadWriteLock lock = new ReentrantReadWriteLock();
void read(){
lock.readLock().lock();
try{
}finally{
lock.readLock().unlock();
}
}
void write(){
lock.writeLock().lock();
try{
}finally{
lock.writeLock().unlock();
}
}
}
For fun, here's an implementation using the new Java 7 try-with-resources feature.
class RWLock {
class ACLock extends ReentrantLock implements AutoCloseable {
public void close() {
this.unlock();
}
}
private ACLock readLock = ACLock();
private ACLock writeLock = ACLock();
private int numReaders = 0
public AutoCloseable write() {
readLock.lock();
writeLock.lock();
return new AutoCloseable() {
public void close() {
writeLock.close();
readLock.close();
}
}
}
public AutoCloseable read() {
try (ACLock read = readLock.acquire()) {
if (numReaders == 0) {
writeLock.lock();
}
numReaders++;
}
return new AutoCloseable() {
public void close() {
numReaders--;
if (numReaders == 0) {
writeLock.unlock();
}
}
}
}
// Here's how you use them
public static void main(String[] args) {
RWLock lock = RWLock();
try (AutoCloseable lock = RWLock.read()) {
// Do some readin'
}
try (AutoCloseable lock = RWLock.write()) {
// Do some writin'
}
}
}