Oliver Scott
Oliver Scott

Reputation: 55

Can a thread change java locked object data if it is not requesting the lock itself?

Oracle's tutorial Intrinsic Locks and Synchronization says:

Intrinsic[Monitor] locks play a role in both aspects of synchronization: enforcing exclusive access to an object's state

I assumed no value in an object could be manipulated by two threads at once, should one thread be executing a 'synchronized' method.

As such, I was surprised (although relieved for the purposes of what I am looking to do) when the following code had the following output. I wasn't entirely sure what to expect, however I figured an error or some point might occur.

From what I can understand, 'synchronized' merely acts to restrict access to an object, if another thread is requesting the monitor state for that object -- but not if that other thread is altering a one off value. Is this correct?

public class HelloWorld implements Runnable{
    Thread t1;
    Thread t2;
    int val1 = 0;
    int val2 = 0;

    public static void main(String[] args) {
        HelloWorld h1 = new HelloWorld();
        h1.t1 = new Thread(h1);
        h1.t2 = new Thread(h1);
        h1.t1.start();
        h1.t2.start();
    }

    @Override
    public void run() {
        System.out.println("STARTED");
        System.gc();
        Thread currentThread = Thread.currentThread();

        if (currentThread == this.t1) { 
            this.locker(); //This is a synchronized method, incrementing val1
        }
        if (currentThread == this.t2) {
            this.adder();  //This is a non-synchronized method, incrementing val2
        }
    }

    private synchronized void locker() {
        for(int i = 0; i < 3; i++){
            val1++;
            System.out.println("LOCKER: " + this.val1);
        }
    }

    private void adder() {
        while(this.val2 < 3) {
            this.val2++;
            System.out.println("ADDER: " + this.val2);
        }
        synchronized(this) {  
        //Synchronize for final output
            System.out.println("FINAL");
            System.out.println(val1);
            System.out.println(val2);     
        }
    }

}

STARTED
STARTED
ADDER: 1
LOCKER: 1
LOCKER: 2
ADDER: 2
LOCKER: 3
ADDER: 3
FINAL
3
3

Upvotes: 3

Views: 903

Answers (2)

RealSkeptic
RealSkeptic

Reputation: 34628

The phrasing "enforcing exclusive access to an object's state" may be misleading. Synchronized code can be used to achieve exclusive access to an object's state, but (a) it doesn't enforce that, and (b) the protected state is not necessarily the state of the object that is being locked. This is demonstrated in the example later on in that tutorial, where the two objects lock1 and lock2 are used to protect the fields c1 and c2 which are not part of their own state.

What synchronized enforces is exclusive access to code - any code that is within a synchronized block on the same monitor can be ran only by the thread that owns that monitor. This can be used to ensure exclusive access to the state - but only if you write your code properly (i.e. you place all access to that state within synchronized blocks).

Nothing prevents you from writing a program that accesses a field from unprotected code, and in that case, exclusive access will not be enforced.

Upvotes: 3

rahsan
rahsan

Reputation: 170

Let me explain to you what is happening to your code:

Thread t1 always executes the locker() method.

Thread t2 always executed the adder() method.

But, since locker() is a synchronized method, that means it holds a lock on the current object, therefore thread t2 will not be able to execute the

synchronized(this){
 ...
}

block until thread t1 finishes executing method locker.

The final value: FINAL 3 3 is printed out by Thread t2, only after t1 prints out "LOCKER: 3"

You might see a different set of output than the one you have posted, like the following:

STARTED
STARTED
ADDER: 1
ADDER: 2
ADDER: 3
FINAL
0
3
LOCKER: 1
LOCKER: 2
LOCKER: 3

This is because Thread t2 reaching the synchronized block(and acquiring lock) in adder() method, before thread t1 could ever acquire lock for synchronized method locker().

Upvotes: 0

Related Questions