Search code examples
javawait

Does wait() release all locks?


I wrote this program to check if a thread_1 holding lock on two different objects: LOCK_OBJECT and FULL goes into waiting mode on LOCK_OBJECT using FULL.wait(). I didn't think the consumer would get the LOCK_OBJECT lock, but the print didn't. So is there anything you missed?

Why does the consumer get the LOCK_OBJECT lock based on the printed results?

This is my code:

public class TestSync {

    private volatile Integer amount = 0;
    private final Object LOCK_OBJECT = new Object();
    private final Object FULL = new Object();

    public void doubleSync() throws InterruptedException {
        System.out.println("Producer  trying to get LOCK_OBJECT lock ");
        synchronized (LOCK_OBJECT) {
            System.out.println("Producer get LOCK_OBJECT lock ");
            Print.sleep(3000);
            while (amount >= 0) {
                synchronized (FULL) {
                    System.out.println("full!");
                    FULL.wait();
                }
            }
            System.out.println("continue~");
            amount++;
        }
    }

    public void simpleSync() {
        System.out.println("Consumer trying to get LOCK_OBJECT lock ");
        synchronized (LOCK_OBJECT) {
            System.out.println("Consumer get LOCK_OBJECT lock");
        }
        System.out.println("Consumer release LOCK_OBJECT lock");
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                int index = atomicInteger.incrementAndGet();
                System.out.println("create no " + index + " thread");
                Thread t = new Thread(r, "one Thread-" + index);
                return t;
            }
        });
        threadPool.execute(() -> {
            try {
                new TestSync().doubleSync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Print.sleep(1000);
        ExecutorService executorService = Executors.newFixedThreadPool(1, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                int index = atomicInteger.incrementAndGet();
                System.out.println("create no " + index + " thread");
                Thread t = new Thread(r, "two Thread-" + index);
                return t;
            }
        });
        executorService.execute(() -> {
            try {
                new TestSync().simpleSync();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

And this is the print result:

create no 1 thread
sleep 1000
Producer  trying to get LOCK_OBJECT lock 
Producer get LOCK_OBJECT lock 
sleep 3000
create no 2 thread
Consumer trying to get LOCK_OBJECT lock 
Consumer get LOCK_OBJECT lock
Consumer release LOCK_OBJECT lock
full!

Solution

  • The code in your question does not compile. What is Print? It appears twice in your code. Once in method main

    Print.sleep(1000);
    

    In the below code, I simply created a class named Print and defined a static method sleep.

    Locks work on objects that are shared between threads. In your code you create a separate TestSync object for each thread. Hence no shared objects. Try creating one instance of TestSync and send it to both threads. Also, you don't need a separate ExecutorService for each thread. You can use a single ExecutorService to launch many threads. The below code demonstrates.

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class TestSync {
        private volatile Integer amount = 0;
        private final Object LOCK_OBJECT = new Object();
        private final Object FULL = new Object();
    
        public void doubleSync() throws InterruptedException {
            System.out.println("Producer  trying to get LOCK_OBJECT lock ");
            synchronized (LOCK_OBJECT) {
                System.out.println("Producer get LOCK_OBJECT lock ");
                Print.sleep(3000);
                while (amount >= 0) {
                    synchronized (FULL) {
                        System.out.println("full!");
                        FULL.wait();
                    }
                }
                System.out.println("continue~");
                amount++;
            }
        }
    
        public void simpleSync() {
            System.out.println("Consumer trying to get LOCK_OBJECT lock ");
            synchronized (LOCK_OBJECT) {
                System.out.println("Consumer get LOCK_OBJECT lock");
            }
            System.out.println("Consumer release LOCK_OBJECT lock");
        }
    
        public static void main(String[] args) throws InterruptedException {
            AtomicInteger atomicInteger = new AtomicInteger(0);
            ExecutorService threadPool = Executors.newFixedThreadPool(2, new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    int index = atomicInteger.incrementAndGet();
                    System.out.println("create no " + index + " thread");
                    Thread t = new Thread(r, "Thread-" + index);
                    return t;
                }
            });
            TestSync ts = new TestSync();
            threadPool.execute(() -> {
                try {
                    ts.doubleSync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            Print.sleep(1000);
            threadPool.execute(() -> {
                try {
                    ts.simpleSync();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            threadPool.shutdown();
        }
    }
    
    class Print {
        public static void sleep(long interval) {
            System.out.println("sleep " + interval);
        }
    }
    

    Note that it is recommended to call method shutdown(), of interface ExecutorService, after you have submitted all your tasks.

    Here is the output when I run the above code.

    create no 1 thread
    Producer  trying to get LOCK_OBJECT lock 
    Producer get LOCK_OBJECT lock 
    sleep 1000
    create no 2 thread
    Consumer trying to get LOCK_OBJECT lock 
    sleep 3000
    full!
    

    As you can see, the consumer does not obtain the lock.