在某个线程方法中对wait()和notify()的调用必须指定一个Object对象,而且该线程必须拥有该Object对象的monitor。而获取对象monitor最简单的办法就是,在对象上使用synchronized关键字。当调用wait()方法以后,该线程会释放掉对象锁,并进入sleep状态。而在其它线程调用notify()方法时,必须使用同一个Object对象,notify()方法调用成功后,所在这个对象上的相应的等侍线程将被唤醒。
对于被一个对象锁定的多个方法,在调用notify()方法时将会任选其中一个进行唤醒,而notifyAll()则是将其所有等待线程唤醒。
package
net.mindview.util;
import
javax.swing.JFrame;
public
class
WaitAndNotify {
public
static
void
main(String[] args) {
System.
out
.println(
"Hello World!"
);
WaitAndNotifyJFrame frame =
new
WaitAndNotifyJFrame();
frame.setDefaultCloseOperation(JFrame.
EXIT_ON_CLOSE
);
// frame.show();
frame.setVisible(
true
);
}
}
@SuppressWarnings
(
"serial"
)
class
WaitAndNotifyJFrame
extends
JFrame {
private
WaitAndNotifyThread
t
;
public
WaitAndNotifyJFrame() {
setSize(300, 100);
setLocation(250, 250);
JPanel panel =
new
JPanel();
JButton start =
new
JButton(
new
AbstractAction(
"Start"
) {
public
void
actionPerformed(ActionEvent event) {
if
(
t
==
null
) {
t
=
new
WaitAndNotifyThread(WaitAndNotifyJFrame.
this
);
t
.start();
}
else
if
(
t
.
isWait
) {
t
.
isWait
=
false
;
t
.n();
// t.notify();
}
}
});
panel.add(start);
JButton pause =
new
JButton(
new
AbstractAction(
"Pause"
) {
public
void
actionPerformed(ActionEvent e) {
if
(
t
!=
null
) {
t
.
isWait
=
true
;
}
}
});
panel.add(pause);
JButton end =
new
JButton(
new
AbstractAction(
"End"
) {
public
void
actionPerformed(ActionEvent e) {
if
(
t
!=
null
) {
t
.interrupt();
t
=
null
;
}
}
});
panel.add(end);
getContentPane().add(panel);
}
}
@SuppressWarnings
(
"unused"
)
class
WaitAndNotifyThread
extends
Thread {
public
boolean
isWait
;
private
WaitAndNotifyJFrame
control
;
private
int
count
;
public
WaitAndNotifyThread(WaitAndNotifyJFrame f) {
control
= f;
isWait
=
false
;
count
= 0;
}
public
void
run() {
try
{
while
(
true
) {
synchronized
(
this
) {
System.
out
.println(
"Count:"
+
count
++);
sleep(100);
if
(
isWait
)
wait();
}
}
}
catch
(Exception e) {
}
}
public
void
n() {
synchronized
(
this
) {
notify();
}
}
|
}
如上面例子方框中的代码,若去掉同步代码块,执行就会抛出
java.lang.IllegalMonitorStateException
异常。
查看JDK,我们可以看到,出现此异常的原因是
当前线程不是此对象监视器的所有者
。
此方法只应由作为此对象监视器的所有者的线程来调用,通过以下三种方法之一,可以使线程成为此对象监视器的所有者:
1,通过执行此对象的同步实例方法,如:
public
synchronized
void
n() {
notify();
}
2,通过执行在此对象上进行同步的synchronized语句的正文,如:
public
void
n() {
synchronized
(
this
) {
notify();
}
}
3,对于Class类型的对象,可以通过执行该类的同步静态方法。
在调用静态方法时,我们并不一定创建一个实例对象。因此,就不能使用this来同步静态方法,所以必须使用Class对象来同步静态方法,由于notify()方法不是静态方法,所以我们无法将n()方法设置成静态方法,所以采用另外一个例子加以说明:
public
class
SynchronizedStatic
implements
Runnable {
private
static
boolean
flag
=
true
;
//类对象同步方法一:
// 注意static修饰的同步方法,监视器:SynchronizedStatic.class
private
static
synchronized
void
testSyncMethod() {
for
(
int
i = 0; i < 100; i++) {
try
{
Thread. sleep(100);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.println(
"testSyncMethod:"
+ i);
}
}
|
//类对象同步方法二:
private
void
testSyncBlock() {
// 显示使用获取class做为监视器.它与static
synchronized
method隐式获取class监视器一样.
synchronized
(SynchronizedStatic.
class
) {
for
(
int
i = 0; i < 100; i++) {
try
{
Thread. sleep(100);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.println(
"testSyncBlock:"
+ i);
}
}
}
|
public
void
run() {
// flag是static的变量.所以,不同的线程会执行不同的方法,只有这样才能看到不同的锁定效果.
if
(
flag
) {
flag
=
false
;
testSyncMethod();
}
else
{
flag
=
true
;
testSyncBlock();
}
}
public
static
void
main(String[] args) {
ExecutorService exec = Executors. newFixedThreadPool(2);
SynchronizedStatic rt =
new
SynchronizedStatic();
SynchronizedStatic rt1 =
new
SynchronizedStatic();
exec.execute(rt);
exec.execute(rt1);
exec.shutdown();
}
}
以上代码的运行结果是,让两个同步方法同时打印从0到99这100个数,其中方法一是一个静态同步方法,它的作用域为类;方法二显示的声明了代码块的作用域是类。这两个方法的异曲同工的。由于方法一和方法二的作用域同为类,所以它们两个方法间是互斥的,也就是说,当一个线程调用了这两个方法中的一个,剩余没有调用的方法也会对其它线程形成阻塞。因此,程序的运行结果会是:
testSyncMethod:0
testSyncMethod:1
... ...
testSyncMethod:99
testSyncBlock:0
... ...
testSyncBlock:99
|
但是,如果我们将方法二中的
SynchronizedStatic.
class
替换成this的话,由于作用域的没,这两个方法就不会形成互斥,程序的输出结果也会交替进行,如下所示:
testSyncBlock:0
testSyncMethod:0
testSyncBlock:1
testSyncMethod:1
... ...
testSyncMethod:99
testSyncBlock:99
|
由上一篇博文,我们已经说明,锁(lock)的作用域有两种,一种是类的对象,另一种的类本身。在以上代码中给出了两种使锁的作用范围为类的方法,这样就可以使同一个类的不同对象之间也能完成同步。
总结以上,需要注意的有以下几点:
1,wait()、notify()、notifyAll()都需要在拥有对象监视器的前提下执行,否则就会抛出
java.lang.IllegalMonitorStateException
异常。
2,多个线程可以同时在一个对象上等待。
3,
notify()是随机唤醒一个在对象上等待的线程,若没有等待的线程,则什么也不做。
4,
notify()唤醒的线程,并不是在
notify()执行以后就立即唤醒,而是在
notify()线程释放了对象监视器之后才真正执行被唤醒的线程。
5,Object的这些方法与Thread的sleep、interrupt方法相差还是很远的,不要混为一谈。
参考资料:
jdk中文文档