繁体   English   中英

Java:wait()从synchronized块释放锁定

[英]Java : Does wait() release lock from synchronized block

我的印象是wait()释放所有锁,但我发现这篇帖子说的

“在同步方法中调用wait是一种获取内部锁的简单方法”

请澄清我有点困惑。

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

“在同步方法中调用wait是一种获取内部锁的简单方法”

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

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

当线程调用等待它释放当前对象锁(它保持所有锁定来自其他对象),然后进入WAITING状态。

当某个其他线程在同一个对象上调用notify或notifyAll时,第一个线程将状态从WAITING更改为BLOCKED,Notified线程不会自动重新获取锁定或变为RUNNABLE,实际上它必须为所有其他被阻塞的线程争取锁定。

WAITING和BLOCKED状态都阻止线程运行,但它们是非常不同的。

必须通过来自其他线程的通知将WAITING线程显式转换为BLOCKED线程。

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

当RUNNABLE线程释放锁定时(通过离开监视器或等待),BLOCKED线程之一自动取代它。

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

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

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

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

wait ::是java.lang.Object类的一部分,所以我们只能在对象上调用此方法。 调用this需要对该对象进行监视(锁定),否则将抛出IllegalMonitorStateException ,例如:Thread.currentThread()。wait()将在下面的代码中抛出此异常。

  Example1 public void doSomething() { Line 1 synchronized(lockObject) { //lock acquired Line 2 lockObject.wait(); // NOT Thread.currentThread().wait() Line 3 } } 

现在在第3行调用wait将释放在第2行获取的锁。因此,进入第1行并等待获取lockObject锁的任何其他线程将获得此锁并继续。

现在让我们考虑这个Example2 ; 这里只释放lockObject2锁,当前线程仍保持lockObject1锁。 这将导致僵局; 所以用户在这种情况下应该更加小心。

  Example2 public void doSomething() { Line 1 synchronized(lockObject1) { //lock1 acquired Line 2 synchronized(lockObject2) { //lock2 acquired Line 3 lockObject2.wait(); Line 4 } } } 

如果这个等待被sleep, yield, or join替换,他们就没有办法解除锁定。 只有等待才能解除它所持有的锁定。

t1.sleep()/t1.yield()中要谨慎,其中是静态api,并且总是会在currentThread上执行action而不是在线程t1

然后让我们了解suspend和这些api的sleep, yield, join之间的区别是什么; 因为suspend被弃用以避免线程持有锁的情况,当它处于挂起(未运行状态)未定义的时间时将导致死锁。 这也是其他api的相同行为。

答案是暂停/恢复将在其他线程上执行,如t1.suspend() ,因为这些api正在挂起Thread.currentThread() 因此,在调用这些api之前,用户必须注意不要保持任何锁定以避免死锁。 调用suspend时不是这种情况。 被调用者线程不知道它将执行挂起的调用者线程(锁定)状态,因此不推荐使用。

我认为这一陈述应该在其全部背景下看待。

当一个线程调用d.wait时,它必须拥有d的内部锁 - 否则抛出错误。 在synchronized方法中调用wait是获取内部锁的一种简单方法。

我明白他们应该简化这个:

调用synchronized方法获取对象的锁定,我们可以简单地在synchronized方法中放入wait()调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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