wait和notify (转)

import java.util.List;


/** 
 * 生产者线程:无数据时再存,存入一个就发通知 
 * @author Administrator 
 * 
 */ 
public class ProduceThread extends Thread{  
    //用来标记放入对象的每一个独立ID号  
    private static int count = 0;  
    //与消费线程或共同存取的对象列表  
    private List shareList;  
      
    public ProduceThread(List shareList){  
        this.shareList = shareList;  
    }  
      
    public void run(){  
        System.out.println("生产线程已经启动..."+shareList.size());  
        while(true){  
            try{  
                Thread.sleep(2000);  
                synchronized(shareList){  
                    while(shareList.size()>0){  
                        shareList.wait();//有货,等待  
                    }  
                    while(shareList.size()==0){//无货,生产  
                        Student st = new Student();  
                        count++;  
                        st.id = count;  
                        st.name = count+"aaa";  
                        System.out.println("--->生产线程放入对象:"+st.toString());  
                        shareList.add(st);  
                        shareList.notify();//通知  
                    }  
                }  
            }catch(Exception ef){  
                ef.printStackTrace();  
            }  
        }  
    }     
}

 

import java.util.List;
import java.util.Map;


/** 
 * 取数据线程:当有数据时取,取完通知 
 * @author Administrator 
 * 
 */ 
public class CustomerThread extends Thread{  
 
    private List shareList;
      
    public CustomerThread(List shareList){  
        this.shareList = shareList;  
    }  
      
    public void run(){  
        System.out.println("消费线程已经启动..."+shareList.size());  
        while(true){  
            try{  
                synchronized(shareList){  
                    while(shareList.size()==0){  
                        //如果没有,消费线程则等待  
                     System.out.println("如果没有,消费线程则等待 ");
                        shareList.wait();  
                    }  
                    while(shareList.size()>0){  
                        System.out.println("<---消费线程取出 "+shareList.remove(0).toString());  
                        //取了一个已经,立即发出通知  
                        shareList.notify(); 
                    }  
                }  
            }catch(Exception ef){  
                ef.printStackTrace();  
            }  
        }  
    }  

import java.util.ArrayList;
import java.util.List;


public class TestPC {

 public static void main(String args[]){
  List shareList = new ArrayList();
  
  ProduceThread a = new ProduceThread(shareList);
  
  a.start();
  
  CustomerThread b = new CustomerThread(shareList);
  b.start();
 }

 
 
}

下面是我原来在优快云论坛上看到的一个贴子,涉及到同步,wait(),notify()等概念的理解,我试着根据原来的一些回复和Think in Java上的相关概念将wait()和notify()这两个方法剖析了一下,欢迎指教.

  问题如下:

  //分析这段程序,并解释一下,着重讲讲synchronized、wait(),notify 谢谢!
  class ThreadA
  {
   public static void main(String[] args)
   {
   ThreadB b=new ThreadB();
   b.start();
   System.out.println("b is start....");
   synchronized(b)//括号里的b是什么意思,起什么作用?
   {
   try
   {
   System.out.println("Waiting for b to complete...");
   b.wait();//这一句是什么意思,究竟让谁wait?
   System.out.println("Completed.Now back to main thread");
   }catch (InterruptedException e){}
   }
   System.out.println("Total is :"+b.total);
   }
  }


  class ThreadB extends Thread
  {
   int total;
   public void run()
   {
   synchronized(this)
   {
   System.out.println("ThreadB is running..");
   for (int i=0;i<100;i++ )
   {
   total +=i;
   System.out.println("total is "+total);
   }
   notify();
   }
   }
  }

  要分析这个程序,首先要理解notify()和wait(),为什么在前几天纪录线程的时候没有纪录这两个方法呢,因为这两个方法本来就不属于Thread类,而是属于最底层的object基础类的,也就是说不光是Thread,每个对象都有notify和wait的功能,为什么?因为他们是用来操纵锁的,而每个对象都有锁,锁是每个对象的基础,既然锁是基础的,那么操纵锁的方法当然也是最基础了.

  再往下看之前呢,首先最好复习一下Think in Java的14.3.1中第3部分内容:等待和通知,也就是wait()和notify了.

  按照Think in Java中的解释:"wait()允许我们将线程置入“睡眠”状态,同时又“积极”地等待条件发生改变.而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒,并检查条件是否有变."

  我们来解释一下这句话.
  "wait()允许我们将线程置入“睡眠”状态",也就是说,wait也是让当前线程阻塞的,这一点和sleep或者suspend是相同的.那和sleep,suspend有什么区别呢?

  区别在于"(wait)同时又“积极”地等待条件发生改变",这一点很关键,sleep和suspend无法做到.因为我们有时候需要通过同步(synchronized)的帮助来防止线程之间的冲突,而一旦使用同步,就要锁定对象,也就是获取对象锁,其它要使用该对象锁的线程都只能排队等着,等到同步方法或者同步块里的程序全部运行完才有机会.在同步方法和同步块中,无论sleep()还是suspend()都不可能自己被调用的时候解除锁定,他们都霸占着正在使用的对象锁不放.
  而wait却可以,它可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.
  但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用,我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!
  好,那怎么把对象锁收回来呢?
  第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.
  第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.

  那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了.
  因此,我们可将一个wait()和notify()置入任何同步方法或同步块内部,无论在那个类里是否准备进行涉及线程的处理。而且实际上,我们也只能在同步方法或者同步块里面调用wait()和notify().

  这个时候我们来解释上面的程序,简直是易如反掌了.

  synchronized(b){...};的意思是定义一个同步块,使用b作为资源锁。b.wait();的意思是临时释放锁,并阻塞当前线程,好让其他使用同一把锁的线程有机会执行,在这里要用同一把锁的就是b线程本身.这个线程在执行到一定地方后用notify()通知wait的线程,锁已经用完,待notify()所在的同步块运行完之后,wait所在的线程就可以继续执行.

<think>好的,我现在需要回答用户关于Java中synchronized、waitnotify的用法及关系的问题。首先,我需要回忆这些关键字的基本概念它们在多线程编程中的作用。 synchronized是Java中用于实现同步的关键字,可以修饰方法或代码块,确保同一时间只有一个线程能执行该段代码。这解决了线程安全问题,防止多个线程同时访问共享资源导致的数据不一致。例如,当多个线程修改同一个变量时,使用synchronized可以确保操作的原子性。 接下来是waitnotify方法。这两个方法属于Object类,必须在synchronized块或方法中使用,否则会抛出IllegalMonitorStateException。wait()的作用是让当前线程释放锁并进入等待状态,直到其他线程调用notify()notifyAll()来唤醒它。而notify()用于唤醒在此对象监视器上等待的单个线程,如果有多个线程在等待,则选择其中一个唤醒。notifyAll()则会唤醒所有等待的线程,让它们竞争锁。 用户可能想知道这些机制的使用场景。例如,生产者-消费者模型是一个经典案例。生产者生产数据时,如果缓冲区已满,就需要等待,直到消费者消费了数据后通知生产者继续生产。同样,消费者在缓冲区为空时等待,直到生产者放入数据后通知消费者。这里,synchronized保护共享缓冲区,waitnotify用于线程间的协调。 需要注意的是,wait()通常放在循环中检查条件,防止虚假唤醒。即使线程被唤醒,也可能条件仍未满足,所以需要再次检查。例如: synchronized (lock) { while (conditionNotMet) { lock.wait(); } // 执行操作 } 此外,wait()可以指定超时时间,避免无限等待。而notify()notifyAll()的选择取决于是否需要唤醒所有等待线程,比如在资源可用时,使用notifyAll()让所有等待线程竞争,可能更公平,但性能开销更大。 用户可能还会疑惑这些方法与LockCondition的区别。Java并发包中的Lock提供了更灵活的锁机制,Condition则允许更细粒度的等待/通知控制,但synchronizedwait/notify是更基础的机制,适用于简单场景。 在引用中提到的外卖小哥例子,说明了在没有协调机制的情况下,线程可能需要不断轮询,效率低下。使用wait/notify可以让线程在条件不满足时等待,而不是忙等,从而节省资源。 总结来说,synchronized用于互斥,确保线程安全;waitnotify用于线程间协作,避免资源浪费。正确使用它们可以编写高效的多线程程序。</think>### Java中synchronized、waitnotify的用法及关系 #### 1. **synchronized的作用与使用场景** - **功能**:`synchronized` 是Java中实现线程同步的关键字,用于修饰代码块或方法,确保同一时刻只有一个线程能执行被保护的代码,从而解决共享资源的线程安全问题[^3]。 - **使用场景**: - 多线程访问共享变量时(如计数器、缓冲区)。 - 需要保证操作的原子性(如银行账)。 - **示例**: ```java public class Counter { private int count = 0; public synchronized void increment() { count++; } } ``` #### 2. **waitnotify的作用与协作** - **功能**: - `wait()`:使当前线程释放锁并进入等待状态,直到其他线程调用`notify()`或`notifyAll()`唤醒它。**必须**在`synchronized`块中使用[^2]。 - `notify()`:唤醒一个等待该对象锁的线程;`notifyAll()`:唤醒所有等待的线程。 - **协作机制**: - 典型场景为**生产者-消费者模型**: - 生产者生产数据时,若缓冲区满,则调用`wait()`等待消费者消费。 - 消费者消费数据后,调用`notify()`唤醒生产者继续生产。 - **示例**: ```java public class Buffer { private Queue<Integer> queue = new LinkedList<>(); private int capacity = 5; public synchronized void produce(int value) throws InterruptedException { while (queue.size() == capacity) { wait(); // 缓冲区满,等待 } queue.add(value); notifyAll(); // 唤醒所有等待的消费者 } public synchronized int consume() throws InterruptedException { while (queue.isEmpty()) { wait(); // 缓冲区空,等待 } int value = queue.poll(); notifyAll(); // 唤醒所有等待的生产者 return value; } } ``` #### 3. **关键区别与注意事项** - **锁的释放**: - `synchronized`执行完毕后自动释放锁。 - `wait()`会**主动释放锁**,而`notify()`仅唤醒线程,**不会释放锁**,需等待同步块结束。 - **条件检查**: - `wait()`应始终在循环中调用,避免虚假唤醒(即使未被通知,线程也可能被唤醒): ```java synchronized (lock) { while (conditionNotMet) { lock.wait(); } // 执行操作 } ``` - **异常处理**: - 若在非同步代码中调用`wait()`或`notify()`,会抛出`IllegalMonitorStateException`[^2]。 #### 4. **典型问题与优化** - **死锁风险**: - 若多个线程互相等待且无唤醒机制,可能导致死锁。需确保逻辑上每个等待最终都能被唤醒。 - **性能优化**: - 使用`notifyAll()`可能更安全,但会引入竞争;若明确只有单个线程需唤醒,则用`notify()`。 - 可结合`java.util.concurrent`包中的高级工具(如`Lock``Condition`)实现更精细的控制。 #### 5. **应用场景** - **生产者-消费者模型**:如任务队列、消息队列。 - **线程池任务调度**:工作线程等待任务时调用`wait()`,提交任务后调用`notify()`。 - **资源池管理**:数据库连接池的分配与回收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值