熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

java多線程notify和wait

2013-11-23 19:41:41  來源: Java高級技術 
java多線程之 wait()notify()notifyAll()
  
  wait()notify()notifyAll()不屬於Thread類而是屬於Object基礎類也就是說每個對像都有 wait()notify()notifyAll()的功能因為都個對像都有鎖鎖是每個對像的基礎當然操作鎖的方法也是最基礎了
  
  先看java doc怎麼說
  
  wait導致當前的線程等待使當前線程進入阻塞狀態(對象等待隊列)直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法當前的線程必須擁有此對象鎖該線程發布對此對象鎖的所有權並等待直到其他線程通過調用 notify 方法或 notifyAll 方法通知在此對象的鎖上等待的線程醒來然後該線程將等到重新獲得對對象鎖的所有權後才能在wait阻塞處開始繼續執行
  
  例子如下
  
  import javautilArrayList;
  
  import javautilList;
  
  class Consume implements Runnable {
  
  private List container = null;
  
  private int count;
  
  public Consume(List lst) {
  
  ntainer = lst;
  
  }
  
  public void run() {
  
  while(true){
  
  synchronized (container) {
  
  if (containersize()==) {
  
  try {
  
  Systemoutprintln(++++++++++++++wait之前
  
  containerwait()// 放棄鎖
  
  Systemoutprintln(++++++++++++++wait之後
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  }
  
  try {
  
  Threadsleep(
  
  } catch (InterruptedException e) {
  
  // TODO Autogenerated catch block
  
  eprintStackTrace()
  
  }
  
  containerremove(
  
  containernotify()
  
  Systemoutprintln(我吃了 + containersize()  +
  
  }
  
  }
  
  }
  
  }
  
  class Product implements Runnable {
  
  private List container = null;
  
  private int count;
  
  public Product(List lst) {
  
  ntainer = lst;
  
  }
  
  public void run() {
  
  while(true) {
  
  synchronized (container) {
  
  if (containersize()>= MultiThreadMAX) {
  
  try {
  
  containerwait()
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  }
  
  try {
  
  Threadsleep(
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  containeradd(new Object())
  
  containernotify()
  
  Systemoutprintln(我生產了 +containersize() +
  
  }
  
  }
  
  }
  
  }
  
  public class MultiThread {
  
  private List container = new ArrayList()
  
  public final static int MAX = ;
  
  public static void main(String args[]) {
  
  MultiThread m = new MultiThread()
  
  new Thread(new Consume(mgetContainer()))start()
  
  new Thread(new Product(mgetContainer()))start()
  
  // new Thread(new Consume(mgetContainer()))start()
  
  // new Thread(new Product(mgetContainer()))start()
  
  }
  
  public List getContainer() {
  
  return container;
  
  }
  
  public void setContainer(List container) {
  
  ntainer = container;
  
  }
  
  }
  
  ++++++++++++++wait之前  //進入消費者線程同時wait 當前線程為阻塞狀態
  
  我生產了個                 //進入生產者線程同時notify告知消費者退出阻塞狀態
  
  //退出同步塊之後生產者和消費者競爭處理器
  
  我生產了個                 // 生產者再次獲得處理器同時notify此時已經沒有線程//在contain上等待則notify不起作用
  
  ++++++++++++++wait之後  // 再次競爭消費者獲得處理器從wait阻塞的地方開始繼續//執行
  
  我吃了
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  ++++++++++++++wait之後
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  我生產了
  
  我生產了
  
  ++++++++++++++wait之後
  
  我吃了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  我生產了
  
  ++++++++++++++wait之後
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我吃了
  
  我吃了
  
  我生產了
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  我生產了
  
  ++++++++++++++wait之後
  
  我吃了
  
  我吃了
  
  我生產了
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  ……
  
  可能的一種輸出結果如上所示
  
  ++++++++++++++wait之前
  
  我生產了
  
  我生產了
  
  ++++++++++++++wait之後
  
  我吃了
  
  我吃了
  
  ++++++++++++++wait之前
  
  我生產了
  
  ++++++++++++++wait之後
  
  notify喚醒在此對象等待隊列上等待的單個線程使線程進入鎖標記等待隊列此時鎖標記等待隊列重新進行鎖的競爭獲得鎖的線程運行同步塊如果所有線程都在此對象上等待則會選擇喚醒其中一個線程直到當前的線程放棄此對象上的鎖定才能繼續執行被喚醒的線程此方法只應由作為此對象鎖的所有者的線程來調用
  
  當前的線程必須擁有此對象鎖此方法只應由作為此對象鎖的所有者的線程來調用說明wait方法與notify方法必須在同步塊內 執行即synchronized(obj之內)


  
  調用對像wait方法後當前線程釋放對像鎖進入對象等待隊列直到其他線程(也只能是其他線程)通過notify 方法或 notifyAll該線程重新進入鎖標記等待隊列接下來進行鎖的競爭若獲得鎖的所有權則繼續執行
  
  記得線程必須重新獲得對像鎖才能繼續執行因為synchronized代碼塊內沒有鎖是寸步不能走的看一個很經典的例子
  
  Code
  
  package ProductAndConsume;
  
  import javautilList;
  
  public class Consume implements Runnable{
  
  private List container = null;
  
  private int count;
  
  public Consume(List lst){
  
  ntainer = lst;
  
  }
  
  public void run() {
  
  while(true){
  
  synchronized (container) {
  
  if(containersize()== ){
  
  try {
  
  containerwait()//放棄鎖
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  }
  
  try {
  
  Threadsleep(
  
  } catch (InterruptedException e) {
  
  // TODO Autogenerated catch block
  
  eprintStackTrace()
  
  }
  
  containerremove(
  
  containernotify()
  
  Systemoutprintln(我吃了+(++count)+
  
  }
  
  }
  
  }
  
  }
  
  package ProductAndConsume;
  
  import javautilList;
  
  public class Product implements Runnable {
  
  private List container = null;
  
  private int count;
  
  public Product(List lst) {
  
  ntainer = lst;
  
  }
  
  public void run() {
  
  while (true) {
  
  synchronized (container) {
  
  if (containersize() > MultiThreadMAX) {
  
  try {
  
  containerwait()
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  }
  
  try {
  
  Threadsleep(
  
  } catch (InterruptedException e) {
  
  eprintStackTrace()
  
  }
  
  containeradd(new Object())
  
  containernotify()
  
  Systemoutprintln(我生產了+(++count)+
  
  }
  
  }
  
  }
  
  }
  
  package ProductAndConsume;
  
  import javautilArrayList;
  
  import javautilList;
  
  public class MultiThread {
  
  private List container = new ArrayList()
  
  public final static int MAX = ;
  
  public static void main(String args[]){
  
  MultiThread m = new MultiThread()
  
  new Thread(new Consume(mgetContainer()))start()
  
  new Thread(new Product(mgetContainer()))start()
  
  new Thread(new Consume(mgetContainer()))start()
  
  new Thread(new Product(mgetContainer()))start()
  
  }
  
  public List getContainer() {
  
  return container;
  
  }
  
  public void setContainer(List container) {
  
  ntainer = container;
  
  }
  
  run()和start()
  
  這兩個方法應該都比較熟悉把需要並行處理的代碼放在run()方法中start()方法啟動線程將自動調用 run()方法這是由Java的內存機制規定的並且run()方法必須是public訪問權限返回值類型為void
  
  二關鍵字Synchronized
  
  這個關鍵字用於保護共享數據當然前提是要分清哪些數據是共享數據每個對象都有一個鎖標志當一個線程訪問該對象時被 Synchronized 修飾的數據將被上鎖阻止其他線程訪問當前線程訪問完這部分數據後釋放鎖標志其他線程就可以訪問了
  
  以下是引用片段
  
  public ThreadTest implements Runnable
  
  {
  
  public synchronized void run(){
  
  for(int i=;i<;i++)
  
  {
  
  Systemoutprintln( + i)
  
  }
  
  }
  
  public static void main(String[] args)
  
  {
  
  Runnable r = new ThreadTest()
  
  Runnable r = new ThreadTest()
  
  Thread t = new Thread(r
  
  Thread t = new Thread(r
  
  tstart()
  
  tstart()
  
  }
  
  }
  
  以上這段程序中的 i 變量並不是共享數據也就是這裡的Synchronized關鍵字並未起作用因為tt兩個線程是兩個對象(rr)的線程不同的對象其數據 是不同的所以r和r兩個對象的i變量是並不是共享數據
  
  當把代碼改成如下Synchronized關鍵字才會起作用
  
  以下是引用片段
  
  Runnable r = new ThreadTest()
  
  Thread t = new Thread(r)
  
  Thread t = new Thread(r)
  
  tstart()
  
  tstart()
  
  三sleep()
  
  使當前線程(即調用該方法的線程)暫停執行一段時間讓其他線程有機會繼續執行但它並不釋放對象鎖也就是如果有Synchronized同步 塊其他線程仍然不同訪問共享數據注意該方法要捕獲異常
  
  比如有兩個線程同時執行(沒有Synchronized)一個線程優先級為MAX_PRIORITY另一個為MIN_PRIORITY如果 沒有Sleep()方法只有高優先級的線程執行完成後低優先級的線程才能執行但當高優先級的線程sleep()後低優先級就有機會執行 了
  
  總之sleep()可以使低優先級的線程得到執行的機會當然也可以讓同優先級高優先級的線程有執行的機會
  
  四join()
  
  join()方法使調用該方法的線程在此之前執行完畢也就是等待調用該方法的線程執行完畢後再往下繼續執行注意該方法也要捕獲異常
  
  五yield()
  
  它與sleep()類似只是不能由用戶指定暫停多長時間並且yield()方法只能讓同優先級的線程有執行的機會
  
  六wait()和notify()notifyAll()
  
  這三個方法用於協調多個線程對共享數據的存取所以必須在Synchronized語句塊內使用這三個方法前面說過Synchronized 這個關鍵字用於保護共享數據阻止其他線程對共享數據的存取但是這樣程序的流程就很不靈活了如何才能在當前線程還沒退出Synchronized數據 塊時讓其他線程也有機會訪問共享數據呢?此時就用這三個方法來靈活控制
  
  wait()方法使當前線程暫停執行並釋放對象鎖標志讓其他線程可以進入 Synchronized數據塊當前線程被放入對象等待池中當調用 notify()方法後將從對象的等待池中移走一個任意的線程並放到鎖標志等待池中只有
  
  鎖標志等待池中的線程能夠獲取鎖標志如果鎖標志等待池中沒有線程則notify()不起作用
  
  notifyAll()則從對象等待池中移走所有等待那個對象的線程並放到鎖標志等待池中


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27303.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.