0

https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html Says "In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other objects' methods. (Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.)"

I actually want to synchronize another object. There are 3 classes, AdminImpl, UserImpl, ServerState.

The same instance of ServerState is an attribute in both AdminImpl and UserImpl.

If I write a critical section in UserImpl with synchronized(this), will it acquire the lock of both UserImpl and the instance of ServerState that is an attribute of UserImpl? Meaning, will AdminImpl not be able to be in a (different) critical section at the same time as UserImpl, because to be in a synchronized section of AdminImpl it needs to acquire both locks (of AdminImpl and ServerState), and can't acquire the second one because it is acquired by UserImpl?

I have found some questions about synchronized such as java method synchronization object Confusing Java synchronized method, synchronized(this), and synchronized class What's the difference between synchronized(this) and synchronized(some other object)

but none touches on this point

chilliefiber
  • 571
  • 2
  • 7
  • 18
  • 2
    No, `synchronized` just locks a single object. – tgdavies Mar 23 '23 at 20:36
  • Could you supply some code? Synchronizing three locks in general is a pretty tricky situation. (C.f. "Dining Philosophers" problem.) It might benefit from further commentary. – markspace Mar 23 '23 at 20:48
  • Still discussing synchronizing more than one lock, note this sentence just after the one you quoted: *"(Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.)"* and it goes on to explain both deadlock and live lock. – markspace Mar 23 '23 at 20:51
  • I think the OP just wants to synchronize their `ServerState` instance. – tgdavies Mar 23 '23 at 20:54
  • @markspace I am aware of the Dining Philosophers problem, I'm just a programmer used to C trying to navigate a Java world :( I didn't supply code because all I needed was this simple fact. – chilliefiber Mar 24 '23 at 10:37

1 Answers1

1

Synchronization doesn't look at the details of the instance which it is locking. It just takes a lock on the "intrinsic lock" which every Java object has.

So it has no effect on the other objects that the first object may hold references to in its fields.

Here's an example which I think illustrates your question.

The ServerState class contains a mutable String, named state, and the method ServerState.addString appends a String to state.

The method addString has been written to guarantee a thread safety problem -- it sleeps between getting the current value of state and assigning the new value to state.

Run the program, and you'll see that the final value of state is admin, when we would expect useradmin.

Make sure that you understand why this is happening.

You can try adding synchronized to the two setStateString methods, and you'll see it makes no difference.

Now just make addString synchronized. That fixes the thread safety problem because now the reading and updating of the state field is atomic -- both operations happen at the same time from the point of view of other threads.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class UserImpl {
    private final ServerState state;

    UserImpl(ServerState state) {
        this.state = state;
    }

    public synchronized void setStateString(String s) {
        state.addString(s);
    }
}

class AdminImpl {
    private final ServerState state;

    AdminImpl(ServerState state) {
        this.state = state;
    }

    public synchronized void setStateString(String s) {
        state.addString(s);
    }
}

class ServerState {
    private volatile String state = "";

    public void addString(String s) {
        String oldState = state;
        System.out.println(Thread.currentThread().getName() + " In addString: adding " + s + ", oldState = " + oldState);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        state = oldState + s;
        System.out.println(Thread.currentThread().getName() + " In addString: set " + state);
    }

    public String getState() {
        return state;
    }
}

public class X {
    public static void main(String[] args) throws InterruptedException {
        ServerState state = new ServerState();
        AdminImpl admin = new AdminImpl(state);
        UserImpl user = new UserImpl(state);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> user.setStateString("user"));
        executorService.submit(() -> admin.setStateString("admin"));
        executorService.shutdown();
        executorService.awaitTermination(2500L, TimeUnit.MILLISECONDS);
        System.out.println(state.getState());
    }
}
tgdavies
  • 10,307
  • 4
  • 35
  • 40