wait() 是否释放所有锁?

Does wait() release all locks?

我编写这个程序是为了检查 thread_1 持有两个不同对象的锁:LOCK_OBJECTFULL 使用 [=15] 在 LOCK_OBJECT 上进入等待模式=].我认为消费者不会获得 LOCK_OBJECT 锁,但印刷品不会。那么你有什么想念的吗?

为什么消费者会根据打印结果得到LOCK_OBJECT锁?

这是我的代码:

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();
            }
        });
    }
}

这是打印结果:

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!

您问题中的代码无法编译。什么是 Print?它在您的代码中出现两次。一旦进入方法 main

Print.sleep(1000);

在下面的代码中,我简单地创建了一个名为 Print 的 class 并定义了一个静态方法 sleep.

Locks 处理线程间共享的对象。在您的代码中,您为每个线程创建一个单独的 TestSync 对象。因此没有共享对象。尝试创建一个 TestSync 实例并将其发送到两个线程。此外,您不需要为每个线程单独设置 ExecutorService。您可以使用单个 ExecutorService 来启动多个线程。下面的代码演示。

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);
    }
}

请注意,建议您在提交所有任务后调用接口 ExecutorService 的方法 shutdown()

这是我 运行 上述代码时的输出。

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!

如你所见,消费者没有获得锁。

Does wait() release all locks?

没有。它只会释放您调用 wait() 的对象的锁。

至于您的代码的行为...我们无法完全解释它,因为它缺少一个关键方法:Print.

但是,您似乎正在使用 TestSync 的多个实例。那将意味着生产者和消费者线程对于各自的锁对象具有不同的对象......并且没有互斥。


使用多个线程池也是一个坏主意(浪费),尤其是动态创建和销毁它们。与一次性线程相比,线程池的主要优点是(重要的)线程创建被分摊。但是当你关闭(或“丢失”)一个线程池时,线程无论如何都会被丢弃......并且必须为“下一个”线程池再次创建。

(这不会影响您的示例的行为。但这是一个坏习惯......如果您在实际应用程序中这样做,它很容易咬到您。)


课程: 使用原始互斥锁和 wait/notify 编程需要格外小心。最好使用更高级别的并发原语......在可能的情况下。

1.It 最好继续执行一个 ExecutorService 实现(因为出于性能原因,我们始终保持有限数量的线程处于活动状态)

2.wait() 确实释放了单个对象的锁,但之后所有线程(考虑相同的优先级)都同样有可能获得锁