注意:这些方法属于Object,而不属于Thread。
每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,他的等待队列是空的。
我们可以利用这些方法来解决“生产者和消费者的问题”。
wait方法呢,是在当前线程锁住对象的锁后,才调用该对象的wait方法的。即在同步代码块中或者同步方法中进行调用的。调用后,该对象的等待队列中就有了一个所在线程,那个线程进入等待状态,此时,只有该对象调用notify方法,才可以把那个线程从队列里面拿出来,使这个线程成为可运行线程。
notifyAll方法就是把该对象等待队列里面的所有线程唤醒,成为可运行线程。
生产者和消费者问题的简单实现:
public class ProductorConsumerProblem {
/**
* @param args
*/
public static void main(String[] args) {
ThreadQueue queue=new ThreadQueue();
Productor productor=new Productor(queue);
Consumer consumer=new Consumer(queue);
productor.start();
consumer.start();
}
}
/*生产者类*/
class Productor extends Thread{
ThreadQueue queue;//等待队列
public Productor(ThreadQueue q){
queue=q;
}
public void run(){
for(int i=1;i<10;i++){
queue.put(i);//把数据放入等待队列中
System.out.println("Productor puts "+i);
}
}
}
class Consumer extends Thread{
ThreadQueue queue;
public Consumer(ThreadQueue q){
queue=q;
}
public void run(){
for(int i=1;i<10;i++){
System.out.println("Consumer gets "+queue.get());//从等待队列中取出数据并显示
}
}
}
class ThreadQueue{
int value;
boolean isFull=false;//false表示是空的
//下面的get和put都是用的是this这个监视器,所以可用notify和wait进行等待与唤醒
public synchronized void put(int i){
if(!isFull){//如果队列是空的,那么就放入数据,把队列的标志设为true(非空),然后调用notify方法,唤醒get的wait
this.value=i;
isFull=true;
notify();//当这个是第一次调用时等待队列里面没有等待的线程,那是不是要判断等待对列是否为空才判断执不执行呢,没必要,因为这个耗费不大,就相当于多执行一条语句,所以没必要判断,如果要写判断语句,那么代码还复杂些,所以这个影响不大的情况下,我们没必要判断。
}
try{
wait();//如果队列是非空的,那么就不要放入数据,就处于等待,一直等待消费者取走数据并调用notify方法
}catch(Exception e){
e.printStackTrace();
}
}
public synchronized int get(){
if(!isFull){
try{
wait();//如果队列是空的,那么就等待,不能取数据
}catch(Exception e){
e.printStackTrace();
}
}
notify();//如果非空,那么取走数据并调用notify方法唤醒put的等待,设置变量为空。注意,因为return后方法就不再继续了,所以要在return之前设置要做的事
isFull=false;
return value;
}
}

如果一个类是线程安全的,那么我们编写线程来调用那个类的方法等的时候,不会出现不同步,死锁等问题,我们要让类是安全的,可以被多线程调用,我们在那个类的方法或者代码块上就要加上关键字synchronized等处理线程同步等安全问题。这些是类设计者要考虑的问题,比如消费者和生产者那个问题中的ThreadQueue就要考虑线程安全问题。所以,设计类是很重要的,比如要有封装性,如果可以多线程访问,要考虑线程安全问题等等。