Should Getters and Setters Be Synchronized

Should getters and setters be synchronized?

I think its best to cite Java Concurrency in Practice here:

It is a common mistake to assume that synchronization needs to be used only when writing to shared variables; this is simply not true.

For each mutable state variable that may be accessed by more than one
thread, all accesses to that variable must be performed with the same
lock held. In this case, we say that the variable is guarded by that
lock.

In the absence of synchronization, the compiler, processor, and runtime can do some downright weird things to the order in which operations appear to execute. Attempts to reason about the order in which memory actions "must" happen in insufflciently synchronized multithreaded programs will almost certainly be incorrect.

Normally, you don't have to be so careful with primitives, so if this would be an int or a boolean it might be that:

When a thread reads a variable without synchronization, it may see a
stale value, but at least it sees a value that was actually placed
there by some thread rather than some random value.

This, however, is not true for 64-bit operations, for instance on long or double if they are not declared volatile:

The Java Memory Model requires fetch and
store operations to be atomic, but for nonvolatile long and double
variables, the JVM is permitted to treat a 64-bit read or write as two
separate 32-bit operations. If the reads and writes occur in different
threads, it is therefore possible to read a nonvolatile long and get
back the high 32 bits of one value and the low 32 bits of another.

Thus, even if you don't care about stale values, it is not safe to use
shared mutable long and double variables in multithreaded programs
unless they are declared volatile or guarded by a lock.

Need of synchronization in getters and setters

My understanding is that in case of simple getter and setters like above (doing simple initialization), I do not need to make these methods synchronized, correct ?

Correct, because what they get and set (an object reference) is treated atomically by the JVM.

The answer would be "No, you do need synchronization" if you were using a long or double and you didn't have it marked volatile.

Both aspects of this are covered in the JLS, §17.7:

17.7. Non-atomic Treatment of double and long

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

How to synchronize getter and setter in threading

Do you need locking at all? Most likely not, according to the limited requirements you've described. But read this to be sure...

  • You have just one thread writing.

    • This means that the variable value can never be "out of date" due to competing writers "clobbering" one another (no possible race condition). So no locking is required to give integrity when considering the individual variable in isolation.
  • You have not mentioned whether some form of atomic, consistent modification of multiple variables is required. I assume it isn't.

    • IF ratio must always be consistent with other variables (e.g. in other objects) - i.e. if a set of variables must change in synchrony as a group with no one reading just part of the changes - then locking is required to give atomic consistency to the set of variables. Then consistent variables must be modified together within in a single locked region and readers must obtain the same lock before reading any of these set of variables (waiting in a blocked state, if necessary).
    • IF ratio can be changed at any time as a lone variable and need not be kept consistent with other variables, then no locking is required give atomic consistency to a set of variables.

Do you need the volatile modifier? Well, yes!

  • You have multiple threads reading.
  • The variable can change at any moment, including an instant before it's about to be read.
  • The volatile modifier is used in multi-threaded apps to guarantee that the value read by "readers" always matches the value written by "writers".

Invoking synchronized getter and setter

Calling the getter and subsequent calling of setter is two independent operations. "Set the result of getter plus one" is not atomic here. So you may perfectly have two gets returning the same value, and two sets of the same value increased by one.

Assume count is 100. You have two threads calls calling the getter, both getting 100. Then they both call the setter, setting 101. So the counter is now 101, not 102 - and both threads "were there" already.

So the result is non-deterministic and depends on the actual order of get/set operations from the two threads.

Should getter and setter on String field of a thread have to be synchronized/

I think I found my answer.
In order to ensure atomic access to a field, you have to declare it volatile.
With it my code become :

package com.afklm.myactu.util;

public class FollowedThread implements Runnable {
private volatile String state;

public void setState(String state) {
this.state = state;
}

public String getState() {
return state;
}

@Override
public void run() {
setState("Step 1 running");
try {
Thread.sleep(1000);
setState("Step 2 running");
Thread.sleep(2000);
setState("Step 3 running");
Thread.sleep(3000);
setState("Thread finished");
} catch (InterruptedException e) {
setState("Thread interrupted");
}
}
}

java synchronized keyword needed on primitive getter / setter method?

The keyword synchronized is one way of ensuring thread-safety. Beware: there's (way) more to thread-safety than deadlocks, or missing updates because of two threads incrementing an int without synchronization.

Consider the following class:

class Connection {
private boolean connected;
synchronized void setConnected(boolean connected){
this.connected = connected;
}
synchronized boolean isConnected(){
return connected;
}
}

If multiple threads share an instance of Connection and one thread calls setConnected(true), without synchronized it is possible that other threads keep seeing isConnected() == false. The synchronized keyword guarantees that all threads sees the current value of the field.

In more technical terms, the synchronized keyword ensures a memory barrier (hint: google that).

In more details: every write made before releasing a monitor (ie, before leaving a synchronized block) is guaranteed to be seen by every read made after acquiring the same monitor (ie, after entering a block synchronizing on the same object). In Java, there's something called happens-before (hint: google that), which is not as trivial as "I wrote the code in this order, so things get executed in this order". Using synchronized is a way to establish a happens-before relationship and guarantee that threads see the memory as you would expect them to see.

Another way to achieve the same guarantees, in this case, would be to eliminate the synchronized keyword and mark the field volatile. The guarantees provided by volatile are as follows: all writes made by a thread before a volatile write are guaranteed to be visible to a thread after a subsequent volatile read of the same field.

As a final note, in this particular case it might be better to use a volatile field instead of synchronized accessors, because the two approaches provide the same guarantees and the volatile-field approach allows simultaneous accesses to the field from different threads (which might improve performance if the synchronized version has too much contention).

Java concurrency - why doesn't synchronizing a setter (but not a getter) make a class thread-safe?

Java has a happens before/happens after memory model. There needs to be some common concurrent construct (e.g. synchronized block/method, lock, volatile, atomic) on both the write path and the read path to trigger this behaviour.

If you synchronize both methods you are creating a lock on the whole object that will be shared by both the read and write threads. The JVM will ensure that any changes that occur on the writing thread that occur before leaving the (synchronized) setInt method will be visible to any reading threads after they enter the (synchronized) getInt method. The JVM will insert the necessary memory barriers to ensure that this will happen.

If only the write method is synchronized then changes to the object may not be visible to any reading thread. This is because there is no point on the read path that the JVM can use to ensure that the reading thread's visible memory (cache's etc.) are in line with the writing thread. Make the getInt method synchronized would provide that.

Note: specifically in this case making the field 'number' volatile would give the correct behaviour as volatile read/write also provides the same memory visibility behaviour in the JVM and the action inside of the setInt method is only an assignment.



Related Topics



Leave a reply



Submit