2012-05-12 37 views
3

我有一個Java的問題。我想寫一個程序,其中有Class Main,它具有一些類(線程任務)的線程的ArrayList,它只寫一個字母和數字。 Object Main只是從ArrayList中喚醒一個線程,讓它做一些事情,而同一個對象(Main)睡眠另一個線程。把可運行的睡覺

它工程確定: 0A,0B,0C,1B,1C,1A,2B,2A,2C,3B,3C,3A,4B,4C,圖4A,圖5B,圖5A,5C,

但只有當我評論的: e.printStackTrace()e是異常 然後 我在java.lang.Object.notify(本機方法) 在Main.run(Main.java得到很多 java.lang.IllegalMonitorStateException :22)

所以通知工作不正確,我該如何正確地喚醒它,請告訴我,顯示,正確。請

import java.util.ArrayList; 

import java.util.ArrayList; 

public class Main extends Thread { 
ArrayList<Thread> threads; 

public Main() { 
    super(); 
    threads = new ArrayList<Thread>(); 
} 

public void run() { 
    for (int i = 0; i < 3; i++) { 
     threads.add(new Thread(new Task(i + 65))); 
    } 
    long cT = System.currentTimeMillis(); 
    for (int i = 0; i < threads.size(); i++) { 
     threads.get(i).start(); 
    } 
    while (System.currentTimeMillis() - cT < 10000) { 
     for (int i = 0; i < threads.size(); i++) { 
      try { 
       threads.get(i).notify(); 
       // HOW TO WAKE THREAD FROM threads ArrayList 
       Thread.sleep(1000); 
       // how to put to bed the same thread ? 
       threads.get(i).wait(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

public static void main(String[] args) { 
    new Main().start(); 
    //new Thread(new Task(65)).start(); 

} 

} 

^h

public class Task implements Runnable { 
int nr; 
char character; 

public Task(int literaASCII) { 
    this.nr = 0; 
    character = (char) (literaASCII); 
} 

@Override 
public void run() { 
    while (true) { 
     try { 
      System.out.print(nr + "" + character + ", "); 
      nr++; 
      int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms) 
      Thread.sleep(r); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 



public static void main(String[] args) { 
    // TODO Auto-generated method stub 

} 
} 
+1

以上是實用上完全錯誤的領導。你究竟在做什麼?有一個單獨的任務線程工作,而其餘的睡眠?並讓主線程按順序使任務線程發生? – alphazero

回答

10

sleepwait有很大的不同。 sleep只是將當前線程暫停指定的時間,並且與其他線程沒有直接交互。

wait更加複雜:中wait的想法是暫停線程給定的顯示器(或鎖定,如果你願意的話),並讓其他線程做顯示器,並釋放其上,直到它notify秒。所以waitnotify涉及兩個或更多線程之間的交互。

由於這種相互作用,爲了waitnotify正常工作的,這就要求這些方法必須擁有監視器(鎖定),這意味着,該object.wait()object.notify()必須從synchronized(object){ ... }塊內被稱爲線程。如果您撥打object.wait()而沒有​​-block,您將始終獲得IllegalMonitorStateException

在你的代碼,

for (int i = 0; i < threads.size(); i++) { 
    threads.get(i).start(); 
} 

這將啓動所有線程,然後在同一時間在同一時間將運行所有這些,不是一個你想要的。

要確保一次只能運行一個線程,您需要將一個通用監視器對象傳遞給所有線程,並在該監視器上顯示wait。例如:

public class Main extends Thread { 
    //... 

    public void run(){ 
    //Initialize all threads with common monitor object 
    Object monitor = new Object(); 
    for (int i = 0; i < 3; i++) { 
     threads.add(new Thread(new Task(i + 65, monitor))); 
    } 
    long cT = System.currentTimeMillis(); 
    for (int i = 0; i < threads.size(); i++) { 
     //All threads will start, and immediately pause on monitor.wait() 
     threads.get(i).start(); 
    } 
    synchronized(monitor){ 
     while (System.currentTimeMillis() - cT < 10000) { 
     //All threads are currently waiting, so we need to wake one random 
     //thread up by calling notify on monitor. Other thread will not run yet, 
     //because this thread still holds the monitor. 
     monitor.notify(); 

     //Make this thread wait, which will temporarily release the monitor 
     //and let the notified thread run. 
     monitor.wait(); 
     } 
    } 
    } 
} 

//... 

public class Task implements Runnable{ 
    int nr; 
    char character; 
    Object monitor; 

    public Task(int literaASCII, Object monitor) { 
    this.nr = 0; 
    this.monitor = monitor; 
    character = (char) (literaASCII); 
    } 

    @Override 
    public void run() { 
    synchronized(monitor){ 
     while (true) { 
     //Pause this thread and let some other random thread 
     //do the work. When other thread finishes and calls notify() 
     //this thread will continue (if this thread is picked). 
     monitor.wait(); 

     try { 
      System.out.print(nr + "" + character + ", "); 
      nr++; 
      int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms) 

      Thread.sleep(r); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     //This thread has finished work for now. 
     //Let one other random thread know. 
     monitor.notify(); 

     //Other thread will not be able to do work until this thread 
     //releases the monitor by calling monitor.wait() or 
     //completely exists the synchronized(monitor){ ... } block. 
     } 
    } 
    } 
} 

它的行爲可能從你的初衷略有不同,因爲線程會醒來隨機,所以也不能保證輸出將在任何特定的順序。除非你有很好的理由使用notify()。因爲notify()只喚醒一個線程,如果該線程在最後調用notify方法「忘記」,所有其他線程永遠可以爲wait

+0

謝謝,但是如何使它像那樣工作 A,A,... A(3000ms),B,B,B,B(3000ms)?代碼在頁面 – Yoda

+1

@RobertKilar獲得精確的排序可能相當複雜。我不太明白爲什麼你會想要AAAABBBB多線程。如果你的線程一次一個地按順序運行,那麼它不是一個真正的多線程程序,是嗎?只是看起來像一個順序程序。 – rodion

+1

您不應該嘗試*殺死*線程。相反,你應該有一個共同的標誌來傳達停止所有線索的意圖。例如,在'Main'中創建'static boolean finished = false',並在想要停止處理時將其設置爲'true'。然後對每個'Task'而不是'while(true){...}'use' while(!Main.finished){...}'。這樣,當run()方法結束時,所有的線程都應該自己死掉。 – rodion

1

你必須要等待()的線程同步:

synchronized(threads.get(i)) { 
    // how to put to bed the same thread ? 
    threads.get(i).wait(); 
} 
+0

爲什麼?你能詳細說明嗎? – noMAD

+0

由於規範文檔如此描述:http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait() – mprivat

2

爲了在對象上調用wait(),你有以保持該對象上的同步鎖(儘管該鎖在線程正在等待時實際上被釋放):

你可以做t他追隨解決方案的地方。

import java.util.ArrayList; 

public class Main extends Thread { 
    ArrayList<Thread> threads; 

    public Main() { 
     super(); 
     threads = new ArrayList<Thread>(); 

     for (int i = 0; i < 3; i++) { 
      threads.add(new Thread(new Task(i + 65))); 
     } 

     for (int i = 0; i < threads.size(); i++) { 
      threads.get(i).start(); 
     } 

    } 

    public void run() { 
     long cT = System.currentTimeMillis(); 
     while (System.currentTimeMillis() - cT < 10000) { 
      for (int i = 0; i < threads.size(); i++) { 
       try { 
        synchronized (threads.get(i)) { 
         threads.get(i).notify(); 
         // HOW TO WAKE THREAD FROM threads ArrayList 
         Thread.sleep(1000); 
         // how to put to bed the same thread ? 
         threads.get(i).wait(); 
        } 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 

    public static void main(String[] args) { 
     new Main().start(); 
     // new Thread(new Task(65)).start(); 

    } 

} 

class Task implements Runnable { 
    int nr; 
    char character; 

    public Task(int literaASCII) { 
     this.nr = 0; 
     character = (char) (literaASCII); 
    } 

    public void run() { 
     while (true) { 
      try { 
       System.out.print(nr + "" + character + ", "); 
       nr++; 
       int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms) 
       Thread.sleep(r); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

} 
0

好,我corected它,所以它現在: 但我得到的控制檯

0A,0B,0C,0D,0E,1A,1B,1C,1D,1E,2A,2B,2C,2D,如圖2E所示,我停止10 我停止11 我停止12 我停止13 我停止14 我停止15

但我寧願像像0A某物,1A(A適用於一些時間3000ms),則其他運行3秒等等,不像A,B,C,D,E,A,但更像是A,A,B,B,B ......

但是我也無法殺死這個線程,也沒有結束循環,我想在Main死的時候殺死它。

感謝您的意見

import java.util.ArrayList;

public class Main extends Thread { 
ArrayList<Thread> threads; 

public Main() { 
    super(); 
    threads = new ArrayList<Thread>(); 
} 

public void run(){ 

Object monitor = new Object(); 
for (int i = 0; i <= 5; i++) { 
    threads.add(new Thread(new Task(i + 65, monitor))); 
} 
long cT = System.currentTimeMillis(); 
for (int i = 0; i < threads.size(); i++) { 
    threads.get(i).start(); 
} 
synchronized(monitor){ 
    while (System.currentTimeMillis() - cT < 10000) { 
     try{ 

    monitor.notify(); 
    Thread.sleep(50); 


    monitor.wait();}catch(Exception e){e.printStackTrace();} 
    } 

for(int i = 0; i < threads.size(); i++){ 
    System.out.println("I suspend "+threads.get(i).getId()); 
    threads.get(i).stop(); 
} 


} 
} 


public static void main(String[] args) { 
    new Main().start(); 
    //new Thread(new Task(65)).start(); 

} 

} 

ħ

public class Task implements Runnable { 
int nr; 
char character; 
Object monitor; 

public Task(int literaASCII, Object monitor) { 
    this.nr = 0; 
    this.monitor = monitor; 
    character = (char) (literaASCII); 
} 

@Override 
public void run() { 
    synchronized (monitor) { 
     while (true) { 

      try { 
       monitor.wait(); 

       System.out.print(nr + "" + character + ", "); 
       nr++; 
       int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms) 

       Thread.sleep(r); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 

      monitor.notify(); 

     } 
    } 
} 

}

+0

如果你想要A,A,A,B,B,B,C,C,C,在多個線程中完成它是完全的廢話(如果線程對你的目的有意義)。如果絕對有必要使用線程,則必須在開始B之前完成A的所有工作,否則如果A完成,則檢入B的任務,如果不是,則將其恢復到睡眠狀態。 –