Abh*_*jit 57 java concurrency multithreading locking wait
我的印象是wait()释放所有锁,但我发现这篇帖子说的
"在同步方法中调用wait是一种获取内部锁的简单方法"
请澄清我有点困惑.
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
coh*_*dar 149
"在同步方法中调用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!");
}
Run Code Online (Sandbox Code Playgroud)
Ole*_*kyi 19
我准备了一个小测试类(一些非常脏的代码,对不起)来证明等待实际释放锁.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上运行此类将返回下一个结果:
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
Run Code Online (Sandbox Code Playgroud)
wait
::是java.lang.Object
类的一部分,因此我们只能在对象上调用此方法.调用this需要监视(锁定)该对象,否则IllegalMonitorStateException
将被抛出,例如)Thread.currentThread().wait()将在下面的代码中抛出此异常.Run Code Online (Sandbox Code Playgroud)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
锁定状态.这将导致僵局; 所以用户在这种情况下应该更加小心.Run Code Online (Sandbox Code Playgroud)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的位置,并且总是在oncurrentThread
on thread上执行操作t1
.然后让我们了解
suspend
这些api 之间的区别是什么sleep, yield, join
; 因为suspend
被弃用以避免线程持有锁的情况,当它处于挂起(未运行状态)未定义的时间时将导致死锁.这也是其他api的相同行为.答案是暂停/恢复将在其他线程上执行,例如
t1.suspend()
这些api暂停的位置Thread.currentThread()
.因此,在调用这些api之前,用户必须注意不要保持任何锁定以避免死锁.调用时不是这种情况suspend
.被调用者线程不知道它将执行挂起的调用者线程(锁定)状态,因此不推荐使用.