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.
Related
There is a shared resource and we need to perform read/write operations on it as per below:
When a write on resource is going on then no read should be allowed.
When a read is going on then no write should be allowed but multiple read threads should be able to read.
I have written code like mentioned below but the problem with this code is all reads will be blocked when a single read thread has acquired the lock. Further i am thinking to use a boolean flag e.g. canReadContinue. Now the first time when read acquires a lock i will flip this flag to true and if it is true then other threads should not try to acquire the lock.
class SharedResource {
Lock writeLock
public Object read() {
writeLock.acquire()
doRead()
}
public void write(Object toBeWritten) {
writeLock.acquire()
doWrite(toBeWritten)
writeLock.release()
}
}
Expected is multiple threads should be able to read when no write is going on.
UPDATED 1 :
public class SharedResource {
private Object writeLock = new Object();
private volatile boolean canReadContinue;
private volatile int readCount;
public void write(Object newState) throws InterruptedException {
synchronized (writeLock) {
// To make sure no read is going on
while (readCount > 0) {
wait();
}
System.out.println("Write thread has the lock.");
doWrite(newState);
}
}
public Object read() {
if(canReadContinue) {
incrementCount();
} else {
synchronized (writeLock) {
System.out.println("Read thread has the lock.");
canReadContinue = true;
incrementCount();
}
}
Object result = doRead();
decrementCount();
if(readCount == 0) {
// TODO - release lock and notify
}
return result;
}
private synchronized void incrementCount() {
readCount++;
}
private synchronized void decrementCount() {
readCount--;
}
private void doWrite(Object newState) {
// do stuff
}
private Object doRead() {
return "";
}
}
Now i need a mechanism to release the lock at line "// TODO - release lock and notify", any pointers how to resolve this issue ?
Hints:
You need a mutex; e.g. a primitive object lock.
You need a counter of the number of readers currently holding a logical read lock.
You need a flag to say if a writer is holding a logical write lock.
You hold the mutex if and only you are acquiring or releasing a logical lock. Once you have acquired it, you release the mutex.
You will need to use wait and notify.
Effectively you need to1 implement a simplified version ReadWriteLock.
1 - ... for the purposes of your homework assignment. In a real world program, you should simply use the existing ReadWriteLock class.
Answering on your updated code here is some skeleton for you to complete:
public class SharedResource {
private final Object signal = new Object();
private boolean writeLocked;
private int readerCount;
public void write(final Object newState) throws InterruptedException {
this.acquireWriteLock();
try {
// Now we know that no read and no other write is going on.
System.out.println("Write thread has the lock.");
this.doWrite(newState);
} finally {
// make sure we release the lock in any case.
this.realeaseWriteLock();
}
}
private void acquireWriteLock() throws InterruptedException {
synchronized (this.signal) {
// Wait until no more readers *and* no writer holds the lock.
// To do: Insert the condition we need to wait for:
while (/* condition here! */ ) {
// To do: Wait for the lock-holding thread(s) to signal that they released their lock(s).
}
this.writeLocked = true; // Let others know that the write lock has been taken.
}
}
private void realeaseWriteLock() {
synchronized (this.signal) {
this.writeLocked = false;
// To do: Notify any and all other waiting threads that we released the lock!
}
}
public Object read() {
// To be done...
}
private void acquireReadLock() throws InterruptedException {
synchronized (this.signal) {
// Wait until no *writer* holds the lock.
// To do: Insert condition we need to wait for:
while (/* condition here! */ ) {
// To do: Wait for the lock-holding thread(s) to signal that they released their lock(s).
}
// Now we know that no writer holds the lock. Acquire (another) read lock:
this.readerCount++;
}
}
private void releaseReadLock() throws InterruptedException {
synchronized (this.signal) {
this.readerCount--;
// To do: Notify any threads waiting (i.e. writer threads).
// (In fact only *required* if there are *no* more readers now because that's the only condition any thread will wait on.)
}
}
private void doWrite(final Object newState) {
// do stuff
}
private Object doRead() {
return "";
}
}
The main point to understand may be that every attempt to take a lock may have to wait, and that every release of a lock should notify any (potential) waiting threads.
Further i am thinking to use a boolean flag e.g. canReadContinue
You're on the right track. But remember that any number of threads could concurrently perform their read accesses and that the write access can only be done if no other thread is currently reading or writing.
So you need to keep track of how many readers are currently holding the lock, and every reader must make sure to release the lock when it's done. Only if & when 0 readers (and 0 writers) hold the lock, a writer may proceed; and only if & when 0 writers hold the lock, any reader may proceed.
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!
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.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
Lock lck=new ReentrantLock();
public void lockIt(){
lck.lock();
for(int i=0;i<10;i++) {
System.out.println("i : "+ i);
try{Thread.sleep(200);} catch (Exception e){}
}
lck.unlock();
}
public void test()
{
synchronized(this) **// mark 1**
{
for(int j=0;j<10;j++)
{
System.out.println("val is"+j);
try{Thread.sleep(200);}catch (Exception e){}
}
}
}
public static void main(String[] args) {
LockTest obj=new LockTest();
new Thread(new Runnable() {
#Override
public void run() {
obj.lockIt();
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
obj.test();
}
}).start();
}
}
}
// In above case when we say lck.lock(); then lock is acquired on
which object actually ? is it "this" or the "lck" itself ?
even if the lock gets acquired on either of "this" or "lck" object
then how diff. threads are able to enter into the critical sections
locked by the same "this" or "lck" object.
case 1: when we use synchronized(this) at mark1..
case 2: when we use synchronized(lck ) at mark 1..
in both the cases both the loops runs in parallel.
The lock obtained by lck.lock() (which is on the lck object) is completely unrelated to the lock obtained by synchronized(this) or synchronized(lck).
If you want to protect critical sections, then all threads have to use the same locking mechanism (and the same lock).
java.util.concurrent.locks.Lock is a different mechanism introduced for cases where the synchronized keyword is not flexible enough. In particular, the synchronized keyword automatically obtains and releases locks as execution enters and leaves a block. It is not possible there to obtain a lock in one method, store it somewhere, leave the method and release the lock sometime later. With the Lock object you can do these things (and it also offers lock wait timeouts, whereas synchronized will potentially block forever).
#Thilo has answered most of it, just to add one point from your code, ensure that you are unlocking in the finally block as shown below, otherwise, there is a possibility that you will be ending up with a dead lock.
public void lockIt(){
lck.lock();
try {
for(int i=0;i<10;i++) {
System.out.println("i : "+ i);
try{Thread.sleep(200);}catch (Exception e){}
}
} finally { //important
if(lck != null) {
lck.unlock();
}
}
}
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'
}
}
}