开启两个线程,一个线程打印1到100的奇数。如1,3,7…99. 另外一个线程打印1到100的偶数。如2,4,6…100.
1到100的数字最终打印出来格式是1,2,3,4,5…100.**
static int count = 0;
static SingletonDemo1 s = new SingletonDemo1();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
synchronized (s) {
while(count<=100) {
System.out.println(count++);
s.notify();
}
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s) {
while(count<=100) {
System.out.println(count++
);
s.notify();
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}).start();
}
总结:
1.wait和notify/notifyAll方法必须在在同步代码块中使用,为什么?
java设计者为了避免wait和notify之间产生竞态条件,所以强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。
调用某个对象的wait()方法能让当前线程阻塞,前提是当前线程必须拥有此对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
2.来实现线程间的通信,为什么wait、notify 和 notifyAll这些方法不在Thread类里面?
我想主要原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
3.执行过程
s数组作为对象锁,thread 1首先进入synchronized的同步块中,thread 2阻塞在s的对象锁。thread 1进入while循环打印日志,执行s.notify方法,这里会唤醒等待s对象锁资源的线程,待thread 1执行完同步块的内容会自动释放锁资源接着打印日志,执行s.wait方法,thread 1交出当前的对象锁,阻塞在此,以至于后续的s end还未打印。
thread 2终于拿到了s锁资源,开始执行while,打印日志,执行s.notify方法,同样的唤醒thread 1,但得走完才能移交锁,接着打印日志,执行s.wait,thread 2阻塞在此,移交锁资源给thread 1。 thread 1接到资源后,从原来阻塞地点又开始运行了,打印s end日志,进入下一次循环…… 如此往复。
值得注意的是,调用notify或notifyAll方法后,当前线程并不会立即放弃锁的持有权,而必须要等待当前同步代码块执行完或者调用了wait方法才会让出锁资源。