Avatar of Rohit Bajaj
Rohit Bajaj
Flag for India asked on

Why is getter method required to be synchronized for the class to be Thread Safe

Hi,
Here is an example from java concurrency in practice making a class Thread Safe.


Screen-Shot-2019-03-11-at-6.27.15-PM.pngI want to understand the following in it -
1) Why is synchronized required on get method ?
Logically it looks like if any time set is called since its synchronized so the
value variable should also be visible to other threads. The latest updated value.

Then why is the get method required to be synchronized ?
Any example where thread safety might break if we dont synchronize get method ?

2) Whats the use of @GuardedBy annotation is it mandatory or optional ?
Is it just a marker thing to tell that its access is inside synchronized method ??

Thanks
Java

Avatar of undefined
Last Comment
girionis

8/22/2022 - Mon
girionis

1) It is not. Only the set method needs to be synchronised.
2) It specifies the the field "value" should only be accessed when a specified lock is held (in your case "this", i.e. the containing object (the object of which the field "value" is a member)).
Rohit Bajaj

ASKER
Hi,
Here is a complete snapshot of the code.
This is from the book  Java concurrency in practice.
It mentions that getter should be synchronized.

Screen-Shot-2019-03-12-at-2.34.18-PM.png
I guess its something like this but not sure.
Since if get is not synchronized one thread could be in set method and one thread can be in get method at the same time.
consider a case -
Thread 1 - inside set
Thread 2 - inside get
thread 3 - going to call get

Suppose Thread 2 gets executed first it will have old value after that set gets executed.
now value of Thread3 will depend on the order of execution of set and get method.
ITs not consistent. it could be new value or even old value.
So if two different threads call get at the same time they may get entirely different result
Or a thread could see a stale value even though set has been executed completely.

This is what i think...
Rohit Bajaj

ASKER
But i still have some confusion. As he mentions that there is a possibility of seeing stale values.
There are two things here
1) The Java Memory Model which caches values and so the latest value is not visible to other threads.

2) The stale value happens because of two threads being simultaneously inside set and get .
eg - Set sets 100 as a value but get returns the old value because it already fetched it from inside the get.

I dont know which stale value is the book talking about here.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
girionis

Yes this could happen. The safest way is to define an AtomicInteger or synchronise both methods, or use the GuardedBy (I think this actually synchronises both methods)..
SOLUTION
girionis

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Rohit Bajaj

ASKER
Also there is one strange thing.
I wrote one class :
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test implements  Runnable{
    private static SynchronizedInteger synchronizedInteger = new SynchronizedInteger();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        Runnable runnable = new Test();
        executor.execute(runnable);
        System.out.println("After runnable");
        synchronizedInteger.set(100);
        System.out.println("After set 100");
    }


    @Override
    public void run() {
        while(synchronizedInteger.get()!=100) {
            System.out.println("not 100");
        }
        System.out.println("its 100");
    }
}

Open in new window

I modified the class to following:
public class SynchronizedInteger {
   private int value;

  public  int get() {
    return value;
  }
  public  void set(int value) {
    this.value = value;
  }

}

Open in new window


ie. removed all synchronization.

But still i see in the output proper values.
I mean for stale values to happen i should see something like
After set 100
not 100
not 100
not 100
.
.
.
then at some point it may get the latest value and prints
its 100

But i never see such a case at least i tried running the code 20 times in Intellij Idea but it doesnt happen.

so even if i made all the values synchronized or not practically i dont get stale values at all may be i am getting lucky or there is some reason behind it.
ASKER CERTIFIED SOLUTION
ste5an

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
girionis

But i never see such a case at least i tried running the code 20 times in Intellij Idea but it doesnt happen.

This is because you only have one thread. Try it with the following:

public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        Runnable runnable = new Test();
Runnable runnable2 = new Test();
Runnable runnable3 = new Test();
        executor.execute(runnable);
        executor.execute(runnable2);
        executor.execute(runnable3);
        System.out.println("After runnable");
        synchronizedInteger.set(100);
        System.out.println("After set 100");
    }

Open in new window

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
SOLUTION
mccarl

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
ste5an

@GuardedBy defines the lock object:

• this : The intrinsic lock of the object in whose class the field is defined.

• class-name.this : For inner classes, it may be necessary to disambiguate 'this'; the class-name.this designation allows you to specify which 'this' reference is intended
• itself : For reference fields only; the object to which the field refers.
• field-name : The lock object is referenced by the (instance or static) field specified by field-name.
• class-name.field-name : The lock object is reference by the static field specified by class-name.field-name.
• method-name() : The lock object is returned by calling the named nil-ary method.
• class-name.class : The Class object for the specified class should be used as the lock object.
girionis

And to clarify @GuardedBy, it is optional and it does not have any effect on the code

I didn't know this, thanks for clarifying it.