Volatile or Synchronized in java - java

We have a class CalcStrategySet, which is accessed in a concurrent environment by multiple threads. I was wondering if you guys can advise which implementation is better from the 2 listed below. In the first one I have declared the variables as volatile and made the flip method, which is atomic operation as Synchronized. Other implementation I have not declared the variables as volatile but made all other methods as synchronized.
Do these methods serve the purpose and not result in race condition and if yes then which one is better?
WITH VOLATILE & SYNCHRONIZED ATOMIC METHOD
public class CalcStrategySet {
private volatile CalcStrategy current;
private volatile CalcStrategy backup;
private volatile boolean isBackup;
public CalcStrategySet(CalcStrategy current, CalcStrategy backup) {
this.current = current;
this.backup = backup;
}
public void isStandard() {
return !isBackup;
}
public void merge(CalcStrategy other) {
current.merge(other);
}
public synchronized void flip() {
if(!isBackup) {
current = backup;
backup = null;
isBackup = true;
} else {
throw new IllegalStateException("Already in backup mode");
}
}
}
FULLY SYNCHRONIZED
public class CalcStrategySet {
private CalcStrategy current;
private CalcStrategy backup;
private boolean isBackup;
public CalcStrategySet(CalcStrategy current, CalcStrategy backup) {
this.current = current;
this.backup = backup;
}
public synchronized void isStandard() {
return !isBackup;
}
public synchronized void merge(CalcStrategy other) {
current.merge(other);
}
public synchronized void flip() {
if(!isBackup) {
current = backup;
backup = null;
isBackup = true;
} else {
throw new IllegalStateException("Already in backup mode");
}
}
}

Do these methods serve the purpose and not result in race condition and if yes then which one is better?
It's hard to determine this without knowing fully how the class is used as well as what the CalcStrategy.merge() method does. If the merge() is reentrant already then the first mechanism seems adequate. There are certainly race conditions that may mean that the current gets updated right before the flip(...) method gets to the switch.
The worry is that the flip(...) method might move to the backup but have not switched isBackup to be true. For example, one thread could be in the middle of flip(...), the current has been set to the backup, but another thread might still see isStandard() returning true because isBackup has not been set yet. This is exacerbated with the multiple volatile fields.
A way to improve this would be to use an AtomicReference for the current. That would reduce that race condition and means that flip() doesn't need to be synchronized. In the below code, standard and backup are final and not volatile. Not sure if that breaks something. There still is the chance that isStandard() gets called right before flip() makes the change but it is an improvement.
Something like:
private final AtomicReference<CalcStrategy> current = new AtomicReference<>();
private final CalcStrategy standard;
private final CalcStrategy backup;
// no boolean
public CalcStrategySet(CalcStrategy standard, CalcStrategy backup) {
this.current.set(standard);
this.standard = standard;
this.backup = backup;
}
public void isStandard() {
// no race with isBackup set in flip()
return (current.get() == standard);
}
// no need for synchronized because one single atomic operation
public void flip() {
// update to the backup atomically
if(!current.compareAndSet(standard, backup)) {
throw new IllegalStateException("Already in backup mode");
}
}

Related

Does synchronisation with ConcurrentHashMap's .compute() guarantee visibility?

Within ConcurrentHashMap.compute() I increment and decrement some long value located in shared memory. Read, increment/decrement only gets performed within compute method on the same key.
So the access to long value is synchronised by locking on ConcurrentHashMap segment, thus increment/decrement is atomic. My question is: Does this synchronisation on a map guarantee visibility for long value? Can I rely on Map's internal synchronisation or should I make my long value volatile?
I know that when you explicitly synchronise on a lock, visibility is guaranteed. But I do not have perfect understanding of ConcurrentHashMap internals. Or maybe I can trust it today but tomorrow ConcurrentHashMap's internals may somehow change: exclusive access will be preserved, but visibility will disappear... and it is an argument to make my long value volatile.
Below I will post a simplified example. According to the test there is no race condition today. But can I trust this code long-term without volatile for long value?
class LongHolder {
private final ConcurrentMap<Object, Object> syncMap = new ConcurrentHashMap<>();
private long value = 0;
public void increment() {
syncMap.compute("1", (k, v) -> {
if (++value == 2000000) {
System.out.println("Expected final state. If this gets printed, this simple test did not detect visibility problem");
}
return null;
});
}
}
class IncrementRunnable implements Runnable {
private final LongHolder longHolder;
IncrementRunnable(LongHolder longHolder) {
this.longHolder = longHolder;
}
#Override
public void run() {
for (int i = 0; i < 1000000; i++) {
longHolder.increment();
}
}
}
public class ConcurrentMapExample {
public static void main(String[] args) throws InterruptedException {
LongHolder longholder = new LongHolder();
Thread t1 = new Thread(new IncrementRunnable(longholder));
Thread t2 = new Thread(new IncrementRunnable(longholder));
t1.start();
t2.start();
}
}
UPD: adding another example which is closer to the code I am working on. I would like to remove map entries when no one else is using the object. Please note that reading and writing of the long value happens only inside of remapping function of ConcurrentHashMap.compute:
public class ObjectProvider {
private final ConcurrentMap<Long, CountingObject> map = new ConcurrentHashMap<>();
public CountingObject takeObjectForId(Long id) {
return map.compute(id, (k, v) -> {
CountingObject returnLock;
returnLock = v == null ? new CountingObject() : v;
returnLock.incrementUsages();
return returnLock;
});
}
public void releaseObjectForId(Long id, CountingObject o) {
map.compute(id, (k, v) -> o.decrementUsages() == 0 ? null : o);
}
}
class CountingObject {
private int usages;
public void incrementUsages() {
--usages;
}
public int decrementUsages() {
return --usages;
}
}
UPD2: I admit that I failed to provide the simplest code examples previously, posting a real code now:
public class LockerUtility<T> {
private final ConcurrentMap<T, CountingLock> locks = new ConcurrentHashMap<>();
public void executeLocked(T entityId, Runnable synchronizedCode) {
CountingLock lock = synchronizedTakeEntityLock(entityId);
try {
lock.lock();
try {
synchronizedCode.run();
} finally {
lock.unlock();
}
} finally {
synchronizedReturnEntityLock(entityId, lock);
}
}
private CountingLock synchronizedTakeEntityLock(T id) {
return locks.compute(id, (k, l) -> {
CountingLock returnLock;
returnLock = l == null ? new CountingLock() : l;
returnLock.takeForUsage();
return returnLock;
});
}
private void synchronizedReturnEntityLock(T lockId, CountingLock lock) {
locks.compute(lockId, (i, v) -> lock.returnBack() == 0 ? null : lock);
}
private static class CountingLock extends ReentrantLock {
private volatile long usages = 0;
public void takeForUsage() {
usages++;
}
public long returnBack() {
return --usages;
}
}
}
No, this approach will not work, not even with volatile. You would have to use AtomicLong, LongAdder, or the like, to make this properly thread-safe. ConcurrentHashMap doesn't even work with segmented locks these days.
Also, your test does not prove anything. Concurrency issues by definition don't happen every time. Not even every millionth time.
You must use a proper concurrent Long accumulator like AtomicLong or LongAdder.
Do not get fooled by the line in the documentation of compute:
The entire method invocation is performed atomically
This does work for side-effects, like you have in that value++; it only works for the internal data of ConcurrentHashMap.
The first thing that you miss is that locking in CHM, the implementation has changed a lot (as the other answer has noted). But even if it did not, your understanding of the:
I know that when you explicitly synchronize on a lock, visibility is guaranteed
is flawed. JLS says that this is guaranteed when both the reader and the writer use the same lock; which in your case obviously does not happen; as such no guarantees are in place. In general happens-before guarantees (that you would require here) only work for pairs, for both reader and writer.

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();

Why the result which the system should print to me changes every time(java concurrency)?

I have a problem in concurrency programming in java. Please look at the code below. The result which system should print to me, changes every time I run the program. Although I’ve synchronized the operation of adding value to sub variable, but the result changes every time. I think I’ve made a mistake somewhere. But I do not know where.
public class Test {
public static void main(String[] args) {
final MyClass mClass = new MyClass();
int size = 10;
final CountDownLatch cdl = new CountDownLatch(size);
for(int i = 0; i < size; i++){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for(int number = 0; number < 100000; number++){
mClass.addToSub(number);
}
cdl.countDown();
}
});
t.start();
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//the result changes every time!!!!!!!!
System.out.println(mClass.getSub());
}
public static class MyClass {
private Long sub = 0L;
public long getSub() {
synchronized (sub) {
return sub;
}
}
public void addToSub(long value){
synchronized (sub) {
sub += value;
}
}
}
}
What you are getting wrong here is not the multi-threading. What is causing this issue is a java feature called auto-boxing.
Your variable sub has the type Long which is a reference to an object (Long and long are different).
You need to have an object to synchronize on in java so you can not use just a normal long.
The problem here is that a Long is immutable meaning the value does not change. So when you do sub += value you are actually doing sub = Long.valueOf(sub.longValue() + value) witch is creating a new object.
So the current thread only has the previous object locked so new threads can still change the reference sub.
What you want to do is synchronize on a reference that wont change, i.e this
public void addToSub(long value){
synchronized (this) {
sub += value;
}
}
Or more terse:
public synchronized void addToSub(long value) {
sub += value;
}
And you should probably use long and not Long.
EDIT
As noted in Thomas Timbuls answer you probably want to use AtomicLong as that gives you thread-safety by default and potentially much better performance (as the threads don't need to wait for each-other).
In addToSub you are changing the value on which you synchronize. Effectively this means that there is no synchronization at all.
Either sync on this, or even better, use AtomicLong and avoid both your problem as well as synchronization overhead (Thread contention):
public static class MyClass {
private AtomicLong sub = new AtomicLong();
public long getSub() {
return sub.get();
}
public void addToSub(long value){
sub.addAndGet(value);
}
}
The Atomic* classes are specifically designed for this type of usecase, where a single variable is updated by multiple Threads, and where synchronize could result in heavy thread contention. If you are dealing with Collections, look towards the ones in java.util.concurrent.*
Edit:
Thanks for the correction of addAndGet vs incrementAndGet.
You're synchronizing on a non-final value:
synchronized (sub) {
This means that as soon as you change it to some other value:
sub += value;
anything which isn't already waiting at the synchronized block can proceed, because nothing is holding the monitor for this new value.
Synchronize on this instead (or some other unchanging value):
synchronized (this) {
sub is an object (Long), change to long and add a private Object for the synchronized. Then it will work.
public static class MyClass {
private Object locker = new Object();
private long sub = 0L;
public long getSub() {
synchronized (locker) {
return sub;
}
}
public void addToSub(long value){
synchronized (locker) {
sub += value;
}
}
}

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.

Synchronization work with conditionals? How can I make this code performant and thread safe?

Given the following code:
public class SomeClass {
private boolean shouldBlock = false;
private Object resource;
public void handleDrawRequest(Canvas canvas) {
if (!shouldBlock && resource == null)
{
shouldBlock = true;
loadTheResource(); //which takes awhile
shouldBlock = false;
}
else if (shouldBlock && resrouce == null)
{
return; //another thread is taking care of the loading of the resource
//and its not ready yet, so just ignore this request
}
drawResourceOn(canvas);
}
}
How can I make this code thread safe? What I'm trying to accomplish is for one and only one thread to load the resource while any other thread attempting access this code at the same time to be discarded (e.g. follow the 'else if' logic) until the resource is loaded. There could be many threads trying to access this code at the same time and I don't want to synchronize the entire method and have a whole stack of threads pile up.
With double checked non-blocking locking:
public class SomeClass {
private Lock lock = new Lock();
private volatile Object resource;
public void handleDrawRequest(Canvas canvas) {
if( resource==null ) {
if( lock.tryLock() ) {
try {
if( resource==null )
resource = loadResource();
}
finally {
lock.unlock();
}
}
else {
return;
}
}
drawResourceOn(canvas);
}
}
If you don't make resource volatile, threads are free to cache it and might never read the updated value. In particular, the second null check will always return true, even if the resource has been loaded after the first one.
You're looking for an AtomicBoolean
public class SomeClass {
// AtomicBolean defaults to the value false.
private AtomicBoolean loadingResource = new AtomicBoolean();
private volatile Object resource;
public void handleDrawRequest(Canvas canvas) {
if (resource == null) {
if (loadingResource.compareAndSet(false, true)) {
loadTheResource(); //which takes awhile
} else {
//another thread is taking care of the loading of the resource
//and its not ready yet, so just ignore this request
return;
}
} else {
drawResourceOn(canvas);
}
}
}

Categories

Resources