Java中的基类Object中,有 notify()
, notifyAll()
, wait(long)
, wait(long, int)
, wait()
方法,在非Thread类型中,可以通过这些方法实现多线程之间的通信和互斥。
在以上方法中,前三个为native实现,wait(long, int)
和wait()
是对wait(long)
的拓展,其中wait()
即是wait(0)
,而wait(long, int)
第二个int类型参数是传入纳秒值,实际情况是当该值大于0的情况下,第一个long型参数值加一,并不会具体实现纳秒级的wait操作。
对象的wait
操作会引起当前线程让出锁,直到该对象的notify
或notifyAll
被调用,或者wait
操作传入的时间过期之后才会重新竞争锁,并在获取到锁之后再继续执行。换一种说法就是调用wait()
方法的线程必须在一个已获取到锁的代码块中调用。按理来说,在synchronized
代码块中能保持属于的一致性,但是在同步代码块中有被锁的对象调用wait()
的情况下,在被锁对象调用wait()
方法时候会释放掉已拥有的锁,在再次获取到锁之后继续执行,也就是说,在有被锁对象的wait()
操作的同步代码块中,从wait()
操作到同步代码块结束才是一个单位的操作,所以如果在同步代码块中被锁对象调用wait()
方法之前对该对象进行的更改都属于非法的操作,即无法保证操作的一致性,但它的其他操作确实允许的,因为该synchronized
只对被锁的对象保证线程安全。
如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 static Integer obj = Integer.valueOf(0);
static Integer ext = Integer.valueOf(0);
class DoWaitThread extends Thread {
public void run() {
try {
synchronized(obj) {
// obj++; // 该操作会在obj调用wait时引起 IllegalMonitorStateException 异常
ext++;
System.out.println("DoWaitThread: before wait... obj=" + obj + ", ext=" + ext);
obj.wait();
obj++;
ext++;
System.out.println("DoWaitThread: after wait... obj=" + obj + ", ext=" + ext);
}
} catch (InterruptedException e) {
System.out.println("DoWaitThread: exception occurred");
e.printStackTrace();
}
}
}
class DoNotifyThread extends Thread {
public void run() {
try {
Thread.sleep(1000);
synchronized(obj) {
System.out.println("DoNotifyThread: before notify... obj=" + obj + ", ext=" + ext);
obj.notifyAll(); // 该通知释放锁是要在该同步块结束之后才会正式释放锁,若无该操作则DoWaitThread的wait之后的操作仍继续等待。
obj++;
ext++;
Thread.sleep(1000);
System.out.println("DoNotifyThread: after notify... obj=" + obj + ", ext=" + ext);
}
} catch (Exception e) {
System.out.println("DoNotifyThread: exception occurred");
e.printStackTrace();
}
}
}
public void testWaitNotify() throws InterruptedException {
Thread w = new DoWaitThread();
Thread n = new DoNotifyThread();
w.start();
n.start();
w.join();
n.join();
System.out.println("obj=" + obj + ", ext=" + ext);
}
以上测试执行结果为:1
2
3
4
5DoWaitThread: before wait... obj=0, ext=1
DoNotifyThread: before notify... obj=0, ext=1
DoNotifyThread: after notify... obj=1, ext=2
DoWaitThread: after wait... obj=2, ext=3
obj=2, ext=3
可见是在DoWaitThread中obj.wait()
之后就让出了锁,由DoNotifyThread获取到锁,在DoNotifyThread中notify,当DoNotifyThread释放锁之后,DoWaitThread继续执行。如果注释掉DoNotifyThread中的obj.notifyAll();
则DoWaitThread将一直处于等待状态。如果将DoWaitThread中的wait()
替换成wait(2000L)
,则在DoNotifyThread执行完之后,DoWaitThread可再次获取到锁,并在超时之后继续执行。如果DoWaitThread中的wait()
替换成wait(500L)
,即DoWaitThread可在DoNotifyThread还未获取到锁之前等待时间超时,则可再获取到锁,则其执行结果为:1
2
3
4
5DoWaitThread: before wait... obj=0, ext=1
DoWaitThread: after wait... obj=1, ext=2
DoNotifyThread: before notify... obj=1, ext=2
DoNotifyThread: after notify... obj=2, ext=3
obj=2, ext=3
Object的
wait()
方法的线程需在该对象上锁的情况下被调用。
notify是唤醒单个正在等待中的线程,如果有多个线程正在等待,则值唤醒一个,而notifyAll是唤醒所有等待的线程,且不管会由哪个线程能竞争到该对象的锁。