How can I synchronize 2 threads to handle data in a list ?
thread A is adding / changing items in a list (writing to the list)
thread B is displaying the items (only reading the list)
I would like to "notify" thread B when it can display the list. In the time of reading the list it must not be changed by thread A. When thread B is done reading, thread A can start changing the list again.
My guesses go to
synchronized(obj)
list.wait() + list.notify()
Threads aren't invoking each other. They run concurrent all the time.
You could put all changes in Runnables and put them in a queue that Thread A executes in order. After each job, A must generate a snapshot of the modified list and submit it to Thread B. You could use Executors for that.
General concept (as I see it in your case) would be as follows.
1) Create an instance of List that you're planning to work with.
2) Write 2 classes corresponding to your thread A and thread B that both implement Runnable and take List as their constructor parameter.
3) Synchronize these 2 classes on list instance:
// method in class that adds
public void add() {
synchronized(list) {
// perform addition ...
list.notify();
}
}
// method in class that reads
public void read() throws InterruptedException {
synchronized(list) {
while (list.isEmpty())
list.wait();
// process data ...
}
}
4) Create 2 threads with argumens corresponding to instances of these 2 classes and start them.
Reader and writer locks are your friends here.
•thread A is adding / changing items in a list (writing to the list)
... so it can use the write lock ...
•thread B is displaying the items (only reading the list)
... so it can use the read lock.
Let's assume that you're using something straight forward for your wait/notify (for example, the built-in Object methods) to block the read and display thread. At that point, your code looks something like this:
/** This is the read/write lock that both threads can see */
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/** This method is called by thread A (the writer / modifier) */
public void add() {
try {
// Only one writer at a time allowed
lock.writeLock().lock();
// Insert code here: Add to the list
} finally {
// Unlock in the finally block to ensure that lock is released
lock.writeLock().unlock();
}
// Notify anyone who's waiting for data
list.notify();
}
/** This method is called by thread B (the reader / displayer) */
public void read() throws InterruptedException {
try {
// As many readers as you like at a time
lock.readLock().lock();
// Insert code here: read from the list
} finally {
// Unlock in the finally block to ensure that lock is released
lock.readLock().unlock();
}
// Wait for new data
list.wait();
}
To make things even more convenient, you can get rid of the notify/wait messaging by using a blocking data structure: e.g., one of the BlockingQueues. In that case, you don't write any notification at all. The reader blocks waiting for new data. When the writer adds data to the queue, the reader unblocks, drains the new data to process, does its thing and then blocks again.
I tried concurrency packages suggested here or here and it works well. The threads lock each other out:
final Lock lock = new ReentrantLock(true);
// thread A
lock.lock();
// write to list
lock.unlock();
// thread B
lock.lock();
// read from list
lock.unlock();
Not sure if they can execute precisely one after another and I didn't get the notify feature. But that doesn't hurt my application.
Related
I have three different threads which creates three different objects to read/manipulate some data which is common for all the threads. Now, I need to ensure that we are giving an access only to one thread at a time.
The example goes something like this.
public interface CommonData {
public void addData(); // adds data to the cache
public void getDataAccessKey(); // Key that will be common across different threads for each data type
}
/*
* Singleton class
*/
public class CommonDataCache() {
private final Map dataMap = new HashMap(); // this takes keys and values as custom objects
}
The implementation class of the interface would look like this
class CommonDataImpl implements CommonData {
private String key;
public CommonDataImpl1(String key) {
this.key = key;
}
public void addData() {
// access the singleton cache class and add
}
public void getDataAccessKey() {
return key;
}
}
Each thread will be invoked as follows:
CommonData data = new CommonDataImpl("Key1");
new Thread(() -> data.addData()).start();
CommonData data1 = new CommonDataImpl("Key1");
new Thread(() -> data1.addData()).start();
CommonData data2 = new CommonDataImpl("Key1");
new Thread(() -> data2.addData()).start();
Now, I need to synchronize those threads if and only if the keys of the data object (passed on to the thread) is the same.
My thought process so far:
I tried to have a class that provides the lock on the fly for a given key which looks something like this.
/*
* Singleton class
*/
public class DataAccessKeyToLockProvider {
private volatile Map<String, ReentrantLock> accessKeyToLockHolder = new ConcurrentHashMap<>();
private DataAccessKeyToLockProvider() {
}
public ReentrantLock getLock(String key) {
return accessKeyToLockHolder.putIfAbsent(key, new ReentrantLock());
}
public void removeLock(BSSKey key) {
ReentrantLock removedLock = accessKeyToLockHolder.remove(key);
}
}
So each thread would call this class and get the lock and use it and remove it once the processing is done. But this can so result in a case where the second thread could get the lock object that was inserted by the first thread and waiting for the first thread to release the lock. Once the first thread removes the lock, now the third thread would get a different lock altogether, so the 2nd thread and the 3rd thread are not in sync anymore.
Something like this:
new Thread(() -> {
ReentrantLock lock = DataAccessKeyToLockProvider.get(data.getDataAccessKey());
lock.lock();
data.addData();
lock.unlock();
DataAccessKeyToLockProvider.remove(data.getDataAccessKey());
).start();
Please let me know if you need any additional details to help me resolve my problem
P.S: Removing the key from the lock provider is kind of mandatory as i will be dealing with some millions of keys (not necessarily strings), so I don't want the lock provider to eat up my memory
Inspired the solution provided #rzwitserloot, I have tried to put some generic code that waits for the other thread to complete its processing before giving the access to the next thread.
public class GenericKeyToLockProvider<K> {
private volatile Map<K, ReentrantLock> keyToLockHolder = new ConcurrentHashMap<>();
public synchronized ReentrantLock getLock(K key) {
ReentrantLock existingLock = keyToLockHolder.get(key);
try {
if (existingLock != null && existingLock.isLocked()) {
existingLock.lock(); // Waits for the thread that acquired the lock previously to release it
}
return keyToLockHolder.put(key, new ReentrantLock()); // Override with the new lock
} finally {
if (existingLock != null) {
existingLock.unlock();
}
}
}
}
But looks like the entry made by the last thread wouldn't be removed. Anyway to solve this?
First, a clarification: You either use ReentrantLock, OR you use synchronized. You don't synchronized on a ReentrantLock instance (you synchronize on any object you want) – or, if you want to go the lock route, you can call the lock lock method on your lock object, using a try/finally guard to always ensure you call unlock later (and don't use synchronized at all).
synchronized is low-level API. Lock, and all the other classes in the java.util.concurrent package are higher level and offer far more abstractions. It's generally a good idea to just peruse the javadoc of all the classes in the j.u.c package from time to time, very useful stuff in there.
The key issue is to remove all references to a lock object (thus ensuring it can be garbage collected), but not until you are certain there are zero active threads locking on it. Your current approach does not know how many classes are waiting. That needs to be fixed. Once you return an instance of a Lock object, it's 'out of your hands' and it is not possible to track if the caller is ever going to call lock on it. Thus, you can't do that. Instead, call lock as part of the job; the getLock method should actually do the locking as part of the operation. That way, YOU get to control the process flow. However, let's first take a step back:
You say you'll have millions of keys. Okay; but it is somewhat unlikely you'll have millions of threads. After all, a thread requires a stack, and even using the -Xss parameter to reduce the stack size to the minimum of 128k or so, a million threads implies you're using up 128GB of RAM just for stacks; seems unlikely.
So, whilst you might have millions of keys, the number of 'locked' keys is MUCH smaller. Let's focus on those.
You could make a ConcurrentHashMap which maps your string keys to lock objects. Then:
To acquire a lock:
Create a new lock object (literally: Object o = new Object(); - we are going to be using synchronized) and add it to the map using putIfAbsent. If you managed to create the key/value pair (compare the returned object using == to the one you made; if they are the same, you were the one to add it), you got it, go, run the code. Once you're done, acquire the sync lock on your object, send a notification, release, and remove:
public void doWithLocking(String key, Runnable op) {
Object locker = new Object();
Object o = concurrentMap.putIfAbsent(key, locker);
if (o == locker) {
op.run();
synchronized (locker) {
locker.notifyAll(); // wake up everybody waiting.
concurrentMap.remove(key); // this has to be inside!
}
} else {
...
}
}
To wait until the lock is available, first acquire a lock on the locker object, THEN check if the concurrentMap still contains it. If not, you're now free to retry this operation. If it's still in, then we now wait for a notification. In any case we always just retry from scratch. Thus:
public void performWithLocking(String key, Runnable op) throws InterruptedException {
while (true) {
Object locker = new Object();
Object o = concurrentMap.putIfAbsent(key, locker);
if (o == locker) {
try {
op.run();
} finally {
// We want to lock even if the operation throws!
synchronized (locker) {
locker.notifyAll(); // wake up everybody waiting.
concurrentMap.remove(key); // this has to be inside!
}
}
return;
} else {
synchronized (o) {
if (concurrentMap.containsKey(key)) o.wait();
}
}
}
}
}
Instead of this setup where you pass the operation to execute along with the lock key, you could have tandem 'lock' and 'unlock' methods but now you run the risk of writing code that forgets to call unlock. Hence why I wouldn't advise it!
You can call this with, for example:
keyedLockSupportThingie.doWithLocking("mykey", () -> {
System.out.println("Hello, from safety!");
});
Let's say I have two threads running like this:
Thread A which performs computation while updating pixels of a shared image
Thread B periodically reads the image and copies it to the screen
Thread A performs work quickly, say 1 million updates per second, so I suspect it would be a bad idea to lock and unlock on a lock/mutex/monitor that often. But if there is no lock and no way of establishing a happens-before relation from thread A to thread B, then by the Java memory model (JMM spec) thread B is not guaranteed at all to see any of A's updates to the image.
So I was thinking that the minimum solution is for threads A and B to both synchronize periodically on the same shared lock, but not actually perform any work while inside the synchronized block - this is what makes the pattern non-standard and dubious. To illustrate in half-real half-pseudo code:
class ComputationCanvas extends java.awt.Canvas {
private Object lock = new Object();
private int[] pixels = new int[1000000];
public ComputationCanvas() {
new Thread(this::runThreadA).start();
new Thread(this::runThreadB).start();
}
private void runThreadA() {
while (true) {
for (1000 steps) {
update pixels directly
without synchornization
}
synchronized(lock) {} // Blank
}
}
private void runThreadB() {
while (true) {
Thread.sleep(100);
synchronized(lock) {} // Blank
this.repaint();
}
}
#Override
public void paint(Graphics g) {
g.drawImage(pixels, 0, 0);
}
}
Does adding empty synchronization blocks in this way correctly achieve the effect of transferring data from thread A to thread B? Or is there some other solution I failed to imagine?
Yes it works. But it works horribly.
Happens before only works when the release of the writer happens before the acquire of the reader. Your implementation assumes that whatever you're writing will complete before the subsequent reading/updating from ThreadB. Causing your data to be flushed all the time by synchronized will cause performance problems, although to what extent I cannot say for sure. Sure, you've made your synchronization finer grained, have you tested it yet?
A better solution might use a singleton/transfer SPSC (single producer/single consumer) queue to store the current snapshot of the writing thread and use that whenever you update.
int[] data = ...
Queue<int[]> queue = new ...
// Thread A
while (true) {
for (1000 iterations or so) {
...
}
queue.add(data);
}
// Thread B
while (true) {
int[] snapshot = queue.take();
this.repaint();
}
The advantage of this is that you don't need to busywait, you can just wait for the queue to block or until the next write. You can skip writes that you don't have time to update. You don't need to depend on the arbitrary thread scheduler to plan data flushes for you.
Remember that thread-safe data structures are great for passing data between threads.
Edit: oops, forgot to say that depending on how your updates go, you might want to use an array copy to prevent your data from being garbled from random writes that aren't cached.
Say we create a thread which runs a synchronized method. This method tries to take() from an empty blocking queue. Now let a separate thread then try to put() and element onto the blocking queue while synchronized on the same object.
This causes a deadlock:
The first thread will not release the lock until an element is added to the queue.
The second thread cannot add an element until the lock is free for it to acquire.
If the two actions need to be atomic and run on separate threads, how can this be achieved without causing a deadlock?
I understand that take() and put() are thread-safe. My question is for when they are used as part of larger actions that must be atomic.
Example:
import java.util.concurrent.*;
public class DeadlockTest {
String input = "Nothing added yet!";
LinkedBlockingQueue<String> buffer = new LinkedBlockingQueue<>();
public synchronized String getFromBuffer() {
System.out.println("Trying to get input from buffer.");
try {
input = buffer.take();
} catch (InterruptedException ex) {}
System.out.println("Got:" + input + "\n");
return input;
}
public static void main(String[] args) throws InterruptedException {
DeadlockTest dl = new DeadlockTest();
new Thread(() -> {
dl.getFromBuffer();
}).start();
// Give new thread time to run.
Thread.sleep(500);
synchronized (dl) {
String message = "Hello, world!";
System.out.println("Adding: " + message);
dl.buffer.put(message);
System.out.println("Added!\n");
System.out.println("Message: " + dl.input);
}
}
}
Say we create a thread which runs a synchronized method. This method tries to take() from an empty blocking queue.
Sounds like bad design. It's usually a mistake to call any blocking methods from within a synchronized method or a synchronized statement.
If the two actions need to be atomic and run on separate threads, how can this be achieved without causing a deadlock?
Well, there's two possibilities:
In one case, the two threads are acting on different data. In that case, they should be using different locks, and they won't interfere with one another at all.
In the other case, the two threads are acting on the same data. In that case, they should lock the same lock, and one thread will have to wait for the other.
Maybe you misunderstand how a blocking queue works. If one thread is waiting to take() something from a blocking queue, that should never prevent another thread from calling put(). That would be the exact opposite of what you want.
What you want (and what you'll get from any of the blocking queue implementations in the Java standard library) is that the put() operation in the second thread will wake up the thread that's waiting to take() something from the queue.
How can I wait for a lock before checking it?
Basically, I want to cache a list in a private variable. I only populate that list every once and a while, the other 99.999999% of the time, I want to read it, so I don't want to lock every time I read.
public class SomeServlet extends CustomServlet {
private static Object locker;
private static List<String> someList;
// moderately heavy populate, not called very often
private void populateList() {
// lock
someList.clear();
someList.addAll(getTheListStuff());
// unlock
}
public void doGetLikeMethod(HttpServletRequest req, HttpServletResponse res) {
// looking at some sort of method to check for the lock
// and wait for it, preferably with a timeout
if(!locker.isLocked(1000) && somelist.isEmpty()) {
populateList();
}
// the lock is present far less than 0.01% of the time this is checked
}
public void updateSomeList() {
populateList(); // populate list for some other reason
}
}
This is in a servlet and is not using a public framework. Our lead is very protective of adding any extra libraries, so I'd like to avoid that if at all possible. We have all the apache and java.util stuff. I'm not sure if I should use some sort of sychronized, ReadWriteLock, ReentReadWriteLock, or Lock.
I think I explained this well enough. Let me know if I need to clarify anything. I may be approaching this entirely wrong.
Use java.util.concurrent.locks.ReentrantReadWriteLock. Multiple threads can hold the read lock at a time, as long as no write is going on, so it satisfies your efficiency desires. Only a single thread can hold the write lock at a time, and only when no threads hold the read lock, so that ensures consistency between writes and reads. You probably want to set fairness on, so that write threads will eventually be able to do their writes even when there is constant contention for reads.
from http://tutorials.jenkov.com/
The rules by which a thread is allowed to lock the ReadWriteLock
either for reading or writing the guarded resource, are as follows:
Read Lock If no threads have locked the ReadWriteLock for writing,
and no thread have requested a write lock (but not yet obtained it).
Thus, multiple threads can lock the lock for reading.
Write Lock If
no threads are reading or writing. Thus, only one thread at a time
can lock the lock for writing.
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
// multiple readers can enter this section
// if not locked for writing, and not writers waiting
// to lock for writing.
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
// only one writer can enter this section,
// and only if no threads are currently reading.
readWriteLock.writeLock().unlock();
So I think it's what you need
In the case where you are writing less and reading more you may use Copy on Write methodology.
I have re-written the code with the solution i have mentioned.
public class SomeServlet extends CustomServlet {
private volatile List<String> someList;
// moderately heavy populate, not called very often
private void populateList() {
someList = getTheListStuff();
}
public void doGetLikeMethod(HttpServletRequest req, HttpServletResponse res) {
if(someList == null) {
//If updating is expensive and do not want to do twice in worst case include the synchronization and another if check.
//If updating is not expensive ignore synchronization and nested if.
synnchroized(this){
if(someList == null) {
populateList();
}
}
}
}
public void updateSomeList() {
populateList(); // populate list for some other reason
}
}
My problem is a little complicated:
I have a concurrent map, the threads want to visit and update the map, if two thread want to fetch the same entry of the map, one should first get the map, update it, and the other should wait until the entry has been updated successfully and then fetch the entry.
My original thought is that: I can use another concurrent map, same key with the targeting map and use a latch as its value.
My code is like:
private final ConcurrentMap<Long, List<RowKeyMap>> targetmap;
private final ConcurrentMap<Long, CountDownLatch> helpermap;
long keyMillis; //key
CountDownLatch restoreLatch = helpermap.get(keyMillis);
if (restoreLatch != null) {
try {
restoreLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted trying to get " + keyMillis);
}
}
List<RowKeyMap> restoredata = targetmap.get(keyMillis);
if (restoredata == null) {
//find the entry should be restored, put a latch into the helpermap and restore it
restoreLatch = new CountDownLatch(1);
CountDownLatch existingLatch = helpermap.putIfAbsent(keyMillis, restoreLatch);
if (existingLatch == null) {
microshards = new ArrayList<>(count);
for (int i = 0; i < count; ++i) {
microshards.add(new RowKeyMap(some parameters));
}
List<RowKeyMap> existing = targetmap.putIfAbsent(keyMillis, microshards);
if (existing == null) {
{do actual restore job here}
} else {
microshards = existing;
}
restoreLatch.countDown();
restoresByDate.remove(keyMillis);
} else {
// Lost the race, wait for the restore task is complete and get the new restoredata
try {
existingLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted trying to get " + keyMillis);
}
{get the new restoredata}
}
}
But the current version has a bug:
Thread A executes through first line, gets null for restoreLatch
Thread B wakes up and executes through first line, also gets null for
restoreLatch
Thread B continues on to following lines, sees existingLatch is null
Thread B continues, puts a created-but-not-yet-restored-into list
into restoredata
Thread A wakes up and executes through, get the
created-but-not-yet-restored-into list from restoredata
Anyone has any ideas to solve this? Thanks!
So what you want is a lock per each map entry. I'm not sure a CountDownLatch is ideal here, because it cannot be re-used, and creating a new one each time complicates your problem.
But your basic problem is that you are not preventing the race condition for the lock itself.
In order to do that, you must first ensure that a lock object for the entry exists, and that if two threads go to the same entry, they will get the same lock.
You can do this by first creating a lock object, then use putIfAbsent to put it in the lock map:
Object entryLock = new Object();
Object returnedLock = helpermap.putIfAbsent( keyMillis, entryLock );
entryLock = returnedLock == null ? entryLock : returnedLock;
What this does is ensure that any two threads that are trying to access the same entry (keyMillis) will get the same lock instance. If thread A is the first to run the putIfAbsent line, then the new object it created in the first line is going to be the one to be placed in the helper map, and it's going to get null back, which means it will also use the object it just placed in the map - entryLock. Thread B then comes along and create its own entryLock. But when it tries the putIfAbsent line, there is already an object mapped to keyMillis, returnedLock, and that's the object it will use (in this case, the original new lock it created will be discarded to the garbage collection).
So whichever order thy get to the putIfAbsent line, they will be using the same lock instance. Now the next step is:
Lock the lock.
Run your processing of the data in the targetMap, creating it if it doesn't exist, updating it if it does, etc. All this time, other threads for this particular keyMillis are waiting, but threads with other keyMillis don't.
Unlock the lock. One of the other threads that wait for this keyMillis entry will now lock the lock.
To do this is pretty simple:
synchronized(entryLock) {
// All operations on the particular entry
}
If you need fancier lock facilities then use ReentrantLock or a CyclicBarrier. A CountDownLatch will need to be replaced with a new one to be usable, and that would defeat the arrangement above, which pretty much relies on the lock object being the same for all threads.
My problem is a little complicated...
Uh oh, I've got bad news for you. If you think that problem is complicated.... You actually have identified the simplest problem in multi-threaded program. It's called mutual exclusion.
You can do it like this in Java:
final Object lock = new Object();
synchronized (lock) {
// some protected statements
}
synchronized (lock) {
// some more protected statements
}
The JVM guarantees that no more than one thread can be in a synchronized(foo) block for the same object foo at the same time.
use the synchronized either at the method declaration or inside a block of code.
see here : https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
Use objects that implement the Lock interface. Java has several of these objects by default that you can use. Here is a tutorial that explains what locks are and how to use them.