简体   繁体   中英

Java: synchronize keyword doesn't block object on different thread

public class SynchronizeTest {

    public synchronized void methodA() {
        System.out.println("calling method a ...");
        sleep(2000);
        System.out.println("ending method a");
    }

    public void methodC() {
        System.out.println("calling method C");
        new Thread(new Runnable() {
            @Override
            public void run() {
                methodA();
            }
        }).start();
        sleep(100);
        System.out.println("end method C");
    }

    public static void main(String[] args) {
        new SynchronizeTest().methodC();
    }


    static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

At first, I guess because I synchronize method A, meaning that whole object will be locked until this method is finished. So the answer should be:

calling method C
calling method a
ending method a
ending method C

But turn out, the result something likes:

calling method C
calling method A
ending method C
ending method A

That means methodA doesn't lock object as I guess. Please tell me why.

When you use the synchronized keyword on an instance method, it means that method on that object can be only invoked by one thread at the time.

That doesn't imply any form of locking on all the object's methods, ie other instance methods can be invoked during that method's invocation - easier too when you invoke it asynchronously as you're doing.

Since you're invoking it within your thread in methodC , and the sleeping time in methodA is much larger than the one before terminating methodC , your current output is very likely to happen every time.

Here's the relevant doc quote (see page here ):

[...]making [...] methods synchronized has two effects:

  • First, it is not possible for two invocations of synchronized methods >on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

  • Second, when a synchronized method exits, it automatically establishes > a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Note

You may want to join the thread invoking methodA , in order to ensure its execution is terminated before printing the last statement in methodC .

First of all here you have 2 thread 1) main and 2) that you create(lets say thread1). now what happens is you make methodA synchronize so when thread1 reaches there it will remain in that method and try to complete execution but at the same time main thread can process further and print last C statement.because you added sleep call in your method which causes thread1 to sleep for 2 second. so out put will be C(main thread) , a(your thread) , C(main thread) , a(your thread)

Read this: https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html As it says, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object. It means that the object will be locked for other synchronized methods. Synchronizing methodC too isn't a good idea either. It will give you the following output: calling method C / calling method a / ending method a / ending method C. In order to get the output you want, you can use the following code:

public class SynchronizeTest implements Runnable{

    public synchronized void methodA() throws InterruptedException {
        System.out.println("calling method a ...");
        Thread.sleep(2000);
        System.out.println("ending method a");
        this.notify();
    }

    public synchronized void methodC() throws InterruptedException {
        System.out.println("calling method C");
        new Thread(new Runnable() {
            @Override
            public void run() {
                methodA();
            }
        }).start();
        this.wait();
        System.out.println("end method C");
    }

    public static void main(String[] args) throws InterruptedException {
        new Test().methodC();
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

    }
}

The keyword synchronized means, that errors that occur, when different threads use the same variables, are being prevented. Locking a thread would make no sense at all, ever since you could just call a method then instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM