I am trying to understand ReadWriteLock. [ This code will just work in your IDE. Copy & paste. Try to do this yourself ]
class ReadWrite {
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
private static final Lock READ_LOCK = LOCK.readLock();
private static final Lock WRITE_LOCK = LOCK.writeLock();
private static final int[] ARR = new int[1];
int i = 0;
Integer read(){
Integer value = null;
try{
READ_LOCK.lock();
value = ARR[0];
}catch (Exception e){
e.printStackTrace();
}finally {
READ_LOCK.unlock();
}
return value;
}
void write(){
try{
WRITE_LOCK.lock();
ARR[0] = i++;
}catch (Exception e){
e.printStackTrace();
}finally {
WRITE_LOCK.unlock();
}
}
}
I was trying to do a performance test.
AtomicInteger atomicInteger = new AtomicInteger(0);
ReadWrite rw = new ReadWrite();
// read 10 millions times
Runnable r1 = () -> IntStream.rangeClosed(1, 10_000_000).forEach(i -> {
if(rw.read() > 0)
atomicInteger.incrementAndGet();
});
Runnable r2 = rw::write;
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Thread[] threads = new Thread[10];
long before = System.currentTimeMillis();
scheduledExecutorService.scheduleAtFixedRate(r2, 1, 1, TimeUnit.MICROSECONDS);
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(r1);
threads[i].start();
}
for (int i = 0; i < 10; i++) {
threads[i].join();
}
System.out.println("Time Taken :: " + (System.currentTimeMillis() - before));
System.out.println("No fo reads :: " + atomicInteger.get());
Ran this test few times.
Case 1:
When i use READ_LOCK for reading it takes 12 seconds to complete. no of reads is 100000000.
Case 2:
When I use WRITE_LOCK for both reading & writing (READ_LOCK not used in this case), it the test takes only 2.5 seconds.
no of reads is 100000000.
I was thinking having separate locks should improve performance.
What is going on here? What is the mistake I do?
You are running read() for 10 millions times (* 10 threads).
and run write() only once ..
The write took 2.5 sec because it was able to take the write lock only when there was no thread with the read lock.
Also, as #Burak was mentioned, you did not measured the right thing here.
You should run same method once with read lock and once with write lock.
Run this method with 10 threads for example.
The method will iterate 1-10 million for example.
In addition, you are calculating the time of creating a thread inside your test (which is not part of the locks mechanism. You should create the threads before)
Then you will see that the write lock method is slower than the read lock.
Why? because when a thread takes the write lock, only this thread will be able to execute the method code.
In case of the read lock, all the 10 threads will run the method in parallel
The documentation of ReadWriteLock mentions this:
Further, if the read operations are too short the overhead of the read-write lock implementation (which is inherently more complex than a mutual exclusion lock) can dominate the execution cost, particularly as many read-write lock implementations still serialize all threads through a small section of code. Ultimately, only profiling and measurement will establish whether the use of a read-write lock is suitable for your application.
Your reads are indeed very fast, so you're observing the overhead that a read-write lock has over a simple lock.
What's involved in the implementation of a read-write lock? To start, there are actually two locks. The read lock may be taken by multiple threads, making it different from a simple reentrant lock, and it must check that the write lock is not locked when trying to lock. The writer lock must check that there are no locked readers when trying to lock, but it's otherwise similar to a single-threaded reentrant lock.
For fine-grained accesses such as in your example, a read-write lock is not worth it. The overhead might become negligible when accessing a bunch of data, such as a "page" of data, e.g. hundreds or thousands of cached database rows.
Related
I'm supernew to multi-threading, but I think I got the overall idea more or less. I'm trying to fill a matrix multi-threaded, but my code is clearly not thread-safe, I have duplicate columns in my matrix, which is not the case when the matrix is filled regularly. Below is an example block of code. Note that the reader is a Scanner object and someOperationOnText(someText) returns an int[100] object.
int[][] mat = new int[100][100];
ExecutorService threadPool = Executors.newFixedThreadPool(8);
for (int i = 0; i < 100; i++) {
Set<Integer>someText = new HashSet<>(reader.next());
int lineIndex = i;
threadPool.submit(() -> mat[lineIndex] = someOperationOnText(someText);
}
Do you see any reason why this is not thread-safe? I can't seem to get my head around it, since the reading is done outside the thread-pool, I didn't think it would be at risk.
Thanks a lot for any debugging tips!
Grts
There is a happens-before between the submit calls and the execution of the lambda by a thread in the executor's thread pool. (See javadoc: "Memory consistency effects"). So that means the lambda will see the correct values for someText, mat and lineIndex.
The only thing that is not thread-safe about this is the (implied) code that uses the values in mat in the main thread. Calling shutdown() on the executor should be sufficient ... though the javadocs don't talk about the memory consistency effects of shutdown() and awaitTermination().
(By my reading of the code for ThreadPoolExecutor, the awaitTermination() method provides a happens-before between the pool threads (after completion of all tasks) and the method's return. The happens-before is due the use of the executor's main lock to synchronize the pool shutdown. It is hard to see how they could implement shutdown correctly without this (or equivalent), so it is more than an "implementation artefact" ... IMO.)
Well, accessing elements in the matrix is a very fast operation, a computing is not.
I think making access to a matrix synchronized while computing can be concurrent is a right approach for you.
int[][] mat = new int[100][100];
Object lock = new Object();
ExecutorService threadPool = Executors.newFixedThreadPool(8);
for (int i = 0; i < 100; i++) {
Set<Integer> someText = new HashSet<>(reader.next());
int lineIndex = i;
threadPool.submit(() -> {
int result = someOperationOnText(someText);
synchronized (lock) {
mat[lineIndex] = result;
}
});
}
Deadlocks only seem possible if there is a cyclic dependency created by the possibility of one or more threads creating a loop through lockable resources.
One option is to avoid these cycles through careful static analysis or through a design pattern for acquiring locks.
However can we prevent deadlocks by using tryLock on the Lock interface?
tryLock attemps to get the lock atomically, and returns true if successful, if its already locked then it returns false so we can simply skip over the code.
int sharedStateA = 0;
int sharedStateB = 0;
Lock lockA = new ReentrantLock();
Lock lockB = new ReentrantLock();
// possible deadlock safe solution
// executed by thread 1
void deadLockSafeUpdateAthenB(){
try {
if (lockA.tryLock()){
sharedStateA = sharedStateA + 1;
try {
if (lockB.tryLock()){
sharedStateB = sharedStateB + 1;
}
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
// executed by thread 2
void deadLockSafeUpdateBthenA(){
try {
if (lockB.tryLock()){
sharedStateB = sharedStateB + 1;
try {
if (lockA.tryLock()){
sharedStateA = sharedStateA + 1;
}
} finally {
lockA.unlock();
}
}
} finally {
lockB.unlock();
}
}
Your code with Lock.tryLock() is deadlock safe but you should try to use the other method,
public boolean tryLock(long timeout,
TimeUnit unit)
if your threads have short run times. The call - tryLock(0,TimeUnit.SECONDS) is better than Lock.tryLock() because it honors fairness policy i.e. lock waiting queue is honored while tryLock() doesn't honor that.
Even if a static analysis tells us that a code is deadlock prone but its not always necessary that a deadlock prone code will actually produce deadlocks since its all an unlucky timing game so your target with tryLock() should be to produce functionally the same program as with deadlock prone code assuming that deadlock doesn't occur.
Fixing one problem shouldn't introduce other issues and in your code it looks quite possible that at some unlucky timing, one thread might not execute at all so I suggest to use timed trylock instead of barging trylock if its mandatory for lock acquisition to be in that order.
Hope it helps !!
I'm trying to implement a fast version of LZ77 and I have a question to ask you about concurrent programming.
For now I have a final byte[] buffer and a final int[] resultHolder, both of the same length. The program does the following:
The main thread writes all the buffer, then notifies the Threads and wait for them to complete.
The single working Thread processes a portion of the buffer saving the results in the same portion of the result holder. Worker's portion is exclusive. After that the main thread is notified and the worker pauses.
When all the workers have paused, the main thread reads the data in resultHolder and updates the buffer, then (if needed) the process begins again from point 1.
Important things in manager (main Thread) are declared as follow:
final byte[] buffer = new byte[SIZE];
final MemoryHelper memoryHelper = new MemoryHelper();
final ArrayBlockingQueue<Object> waitBuffer = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final ArrayBlockingQueue<Object> waitResult = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final int[] resultHolder = new int[SIZE];
MemoryHelper simply wraps a volatile field and provides two methods: one for reading it and one for writing to it.
Worker's run() code:
public void run() {
try {
// Wait main thread
while(manager.waitBuffer.take() != SHUTDOWN){
// Load new buffer values
manager.memoryHelper.readVolatile();
// Do something
for (int i = a; i <= b; i++){
manager.resultHolder[i] = manager.buffer[i] + 10;
}
// Flush new values of resultHolder
manager.memoryHelper.writeVolatile();
// Signal job done
manager.waitResult.add(Object.class);
}
} catch (InterruptedException e) { }
}
Finally, the important part of main Thread:
for(int i=0; i < 100_000; i++){
// Start workers
for (int j = 0; j < TOT_WORKERS; j++)
waitBuffer.add(Object.class);
// Wait workers
for (int j = 0; j < TOT_WORKERS; j++)
waitResult.take();
// Load results
memoryHelper.readVolatile();
// Do something
processResult();
setBuffer();
// Store buffer
memoryHelper.writeVolatile();
}
Synchronization on ArrayBlockingQueue works well. My doubt is in using readVolatile() and writeVolatile(). I've been told that writing to a volatile field flushes to memory all the previously changed data, then reading it from another thread makes them visible.
So is it enough in this case to ensure a correct visibility? There is never a real concurrent access to the same memory areas, so a volatile field should be a lot cheaper than a ReadWriteLock.
You don't even need volatile here, because BlockingQueues already provide necessary memory visibility guarantees:
Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a BlockingQueue happen-before actions subsequent to the access or removal of that element from the BlockingQueue in another thread.
In general, if you already have some kind of synchronization, you probably don't need to do anything special to ensure memory visibility, because it's already guaranteed by synchronization primitives you use.
However, volatile reads and writes can be used to ensure memory visibility when you don't have explicit synchronization (e.g. in lock-free algorithms).
P. S.
Also it looks like you can use CyclicBarrier instead of your solution with queues, it's especially designed for similar scenarios.
My application needs to keep an access log of requests to a certain resource and multiple threads will be recording log entries. The only pertinent piece of information is the timestamp of the request and the stats being retrieved will be how many requests occurred in the last X seconds. The method that returns the stats for a given number of seconds also needs to support multiple threads.
I was thinking of approaching the concurrency handling using the Locks framework, with which I am not the most familiar, hence this question. Here is my code:
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
public class ConcurrentRecordStats
{
private LinkedList<Long> recLog;
private final ReentrantLock lock = new ReentrantLock();
public LinkedConcurrentStats()
{
this.recLog = new LinkedList<Long>();
}
//this method will be utilized by multiple clients concurrently
public void addRecord(int wrkrID)
{
long crntTS = System.currentTimeMillis();
this.lock.lock();
this.recLog.addFirst(crntTS);
this.lock.unlock();
}
//this method will be utilized by multiple clients concurrently
public int getTrailingStats(int lastSecs)
{
long endTS = System.currentTimeMillis();
long bgnTS = endTS - (lastSecs * 1000);
int rslt = 0;
//acquire the lock only until we have read
//the first (latest) element in the list
this.lock.lock();
for(long crntRec : this.recLog)
{
//release the lock upon fetching the first element in the list
if(this.lock.isLocked())
{
this.lock.unlock();
}
if(crntRec > bgnTS)
{
rslt++;
}
else
{
break;
}
}
return rslt;
}
}
My questions are:
Will this use of ReentrantLock insure thread safety?
Is it needed to use a lock in getTrailingStats?
Can I do all this using synchronized blocks? The reason I went with locks is because I wanted to have the same lock in both R and W sections so that both writes and reading of the first element in the list (most recently added entry) is done a single thread at a time and I couldn't do that with just synchronized.
Should I use the ReentrantReadWriteLock instead?
The locks can present a major performance bottleneck. An alternative is to use a ConcurrentLinkedDeque: use offerFirst to add a new element, and use the (weakly consistent) iterator (that won't throw a ConcurrentModificationException) in place of your for-each loop. The advantage is that this will perform much better than your implementation or than the synchronizedList implementation, but the disadvantage is that the iterator is weakly consistent - thread1 might add elements to the list while thread2 is iterating through it, which means that thread2 won't count those new elements. However, this is functionally equivalent to having thread2 lock the list so that thread1 can't add to it - either way thread2 isn't counting the new elements.
I have read article concerning atomic operation in Java but still have some doubts needing to be clarified:
int volatile num;
public void doSomething() {
num = 10; // write operation
System.out.println(num) // read
num = 20; // write
System.out.println(num); // read
}
So i have done w-r-w-r 4 operations on 1 method, are they atomic operations? What will happen if multiple threads invoke doSomething() method simultaneously ?
An operation is atomic if no thread will see an intermediary state, i.e. the operation will either have completed fully, or not at all.
Reading an int field is an atomic operation, i.e. all 32 bits are read at once. Writing an int field is also atomic, the field will either have been written fully, or not at all.
However, the method doSomething() is not atomic; a thread may yield the CPU to another thread while the method is being executing, and that thread may see that some, but not all, operations have been executed.
That is, if threads T1 and T2 both execute doSomething(), the following may happen:
T1: num = 10;
T2: num = 10;
T1: System.out.println(num); // prints 10
T1: num = 20;
T1: System.out.println(num); // prints 20
T2: System.out.println(num); // prints 20
T2: num = 20;
T2: System.out.println(num); // prints 20
If doSomething() were synchronized, its atomicity would be guaranteed, and the above scenario impossible.
volatile ensures that if you have a thread A and a thread B, that any change to that variable will be seen by both. So if it at some point thread A changes this value, thread B could in the future look at it.
Atomic operations ensure that the execution of the said operation happens "in one step." This is somewhat confusion because looking at the code 'x = 10;' may appear to be "one step", but actually requires several steps on the CPU. An atomic operation can be formed in a variety of ways, one of which is by locking using synchronized:
What the volatile keyword promises.
The lock of an object (or the Class in the case of static methods) is acquired, and no two objects can access it at the same time.
As you asked in a comment earlier, even if you had three separate atomic steps that thread A was executing at some point, there's a chance that thread B could begin executing in the middle of those three steps. To ensure the thread safety of the object, all three steps would have to be grouped together to act like a single step. This is part of the reason locks are used.
A very important thing to note is that if you want to ensure that your object can never be accessed by two threads at the same time, all of your methods must be synchronized. You could create a non-synchronized method on the object that would access the values stored in the object, but that would compromise the thread safety of the class.
You may be interested in the java.util.concurrent.atomic library. I'm also no expert on these matters, so I would suggest a book that was recommended to me: Java Concurrency in Practice
Each individual read and write to a volatile variable is atomic. This means that a thread won't see the value of num changing while it's reading it, but it can still change in between each statement. So a thread running doSomething while other threads are doing the same, will print a 10 or 20 followed by another 10 or 20. After all threads have finished calling doSomething, the value of num will be 20.
My answer modified according to Brian Roach's comment.
It's atomic because it is integer in this case.
Volatile can only ganrentee visibility among threads, but not atomic. volatile can make you see the change of the integer, but cannot ganrentee the integration in changes.
For example, long and double can cause unexpected intermediate state.
Atomic Operations and Synchronization:
Atomic executions are performed in a single unit of task without getting affected from other executions. Atomic operations are required in multi-threaded environment to avoid data irregularity.
If we are reading/writing an int value then it is an atomic operation. But generally if it is inside a method then if the method is not synchronized many threads can access it which can lead to inconsistent values. However, int++ is not an atomic operation. So by the time one threads read it’s value and increment it by one, other thread has read the older value leading to wrong result.
To solve data inconsistency, we will have to make sure that increment operation on count is atomic, we can do that using Synchronization but Java 5 java.util.concurrent.atomic provides wrapper classes for int and long that can be used to achieve this atomically without usage of Synchronization.
Using int might create data data inconsistencies as shown below:
public class AtomicClass {
public static void main(String[] args) throws InterruptedException {
ThreardProcesing pt = new ThreardProcesing();
Thread thread_1 = new Thread(pt, "thread_1");
thread_1.start();
Thread thread_2 = new Thread(pt, "thread_2");
thread_2.start();
thread_1.join();
thread_2.join();
System.out.println("Processing count=" + pt.getCount());
}
}
class ThreardProcesing implements Runnable {
private int count;
#Override
public void run() {
for (int i = 1; i < 5; i++) {
processSomething(i);
count++;
}
}
public int getCount() {
return this.count;
}
private void processSomething(int i) {
// processing some job
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
OUTPUT: count value varies between 5,6,7,8
We can resolve this using java.util.concurrent.atomic that will always output count value as 8 because AtomicInteger method incrementAndGet() atomically increments the current value by one. shown below:
public class AtomicClass {
public static void main(String[] args) throws InterruptedException {
ThreardProcesing pt = new ThreardProcesing();
Thread thread_1 = new Thread(pt, "thread_1");
thread_1.start();
Thread thread_2 = new Thread(pt, "thread_2");
thread_2.start();
thread_1.join();
thread_2.join();
System.out.println("Processing count=" + pt.getCount());
}
}
class ThreardProcesing implements Runnable {
private AtomicInteger count = new AtomicInteger();
#Override
public void run() {
for (int i = 1; i < 5; i++) {
processSomething(i);
count.incrementAndGet();
}
}
public int getCount() {
return this.count.get();
}
private void processSomething(int i) {
// processing some job
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Source: Atomic Operations in java