Java : wait() 从同步块中释放锁吗?

我的印象是wait()释放所有锁,但我找到了这篇文章,上面写着

“在同步方法中调用等待是获取固有锁的简单方法”

请澄清我有点困惑。

http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html


答案 1

“在同步方法中调用等待是获取固有锁的简单方法”

这句话是错误的,它是文档中的错误。

线程在进入同步方法时获取内部锁。同步方法中的线程设置为锁的所有者,并且处于 RUNNABLE 状态。任何尝试进入锁定方法的线程都将变为 BLOCKED

当线程调用等待时,它会释放当前对象锁(它保留来自其他对象的所有锁),并且比进入等待状态。

当其他一些线程调用通知或 notifyAll 在同一对象上,第一个线程的状态从“等待”更改为“已阻止”,通知线程不会自动重新获取锁或变为 RUNNABLE,实际上它必须与所有其他被阻止的线程争夺锁。

等待和已阻止状态都阻止线程运行,但它们非常不同。

等待线程必须通过来自其他线程的通知显式转换为阻塞线程。

等待永远不会直接进入RUNNABLE。

当 RUNNABLE 线程释放锁(通过离开监视器或等待)时,一个被阻塞的线程会自动取代它的位置。

因此,总而言之,线程在进入同步方法或在等待重新进入同步方法时获取锁。

public synchronized guardedJoy() {
    // must get lock before entering here
    while(!joy) {
        try {
            wait(); // releases lock here
            // must regain the lock to reentering here
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

答案 2

我准备了一个小的测试类(一些非常脏的代码,对不起),以演示等待实际上释放了锁。

public class Test {
    public static void main(String[] args) throws Exception {
        testCuncurrency();
    }

    private static void testCuncurrency() throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(new WaitTester(lock));
        Thread t2 = new Thread(new WaitTester(lock));
        t1.start();
        t2.start();
        Thread.sleep(15 * 1000);
        synchronized (lock) {
            System.out.println("Time: " + new Date().toString()+ ";" + "Notifying all");
            lock.notifyAll();
        }
    }

    private static class WaitTester implements Runnable {
        private Object lock;
        public WaitTester(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            try {
                synchronized (lock) {
                    System.out.println(getTimeAndThreadName() + ":only one thread can be in synchronized block");
                    Thread.sleep(5 * 1000);

                    System.out.println(getTimeAndThreadName() + ":thread goes into waiting state and releases the lock");
                    lock.wait();

                    System.out.println(getTimeAndThreadName() + ":thread is awake and have reacquired the lock");

                    System.out.println(getTimeAndThreadName() + ":syncronized block have finished");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private static String getTimeAndThreadName() {
        return "Time: " + new Date().toString() + ";" + Thread.currentThread().getName();
    }
}

在我的计算机上运行此类将返回下一个结果:

Time: Tue Mar 29 09:16:37 EEST 2016;Thread-0:only one thread can be in synchronized block
Time: Tue Mar 29 09:16:42 EEST 2016;Thread-0:thread goes into waiting state and releases the lock
Time: Tue Mar 29 09:16:42 EEST 2016;Thread-1:only one thread can be in synchronized block
Time: Tue Mar 29 09:16:47 EEST 2016;Thread-1:thread goes into waiting state and releases the lock
Time: Tue Mar 29 09:16:52 EEST 2016;Notifying all
Time: Tue Mar 29 09:16:52 EEST 2016;Thread-1:thread is awake and have reacquired the lock
Time: Tue Mar 29 09:16:57 EEST 2016;Thread-1:syncronized block have finished
Time: Tue Mar 29 09:16:57 EEST 2016;Thread-0:thread is awake and have reacquired the lock
Time: Tue Mar 29 09:17:02 EEST 2016;Thread-0:syncronized block have finished

推荐