2009-07-27 26 views
0

當您希望某個任務由另一個線程執行時,可以擴展Thread或實現Runnable。Java:完全在第二個線程中運行的類/ IllegalMonitorStateException

我試圖創建一個完全在第二個線程中運行一個類的類。

這意味着你可以調用anyMethod(),它立即返回,並由第二個線程執行。

這裏是我的嘗試:

import java.lang.reflect.Method; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Queue; 
import java.util.concurrent.ConcurrentLinkedQueue; 

/** 
* Extend this class to run method calls asynchronously in the second thread implemented by this class. 
* Create method(type1 param1, type2 param2, ...) and let it call this.enqueueVoidCall("method", param1, param2, ...) 
* 
* The thread executing the run-method will automatically call methodAsync with the specified parameters. 
* To obtain the return-value, pass an implementation of AsyncCallback to this.enqueueCall(). 
* AsyncCallback.returnValue() will automatically be called upon completion of the methodAsync. 
* 
*/ 
public class ThreadedClass extends Thread { 
    private static Object test; 

    private Queue<String> queue_methods = new ConcurrentLinkedQueue<String>(); 
    private Queue<Object[]> queue_params = new ConcurrentLinkedQueue<Object[]>(); 
    private Queue<AsyncCallback<? extends Object>> queue_callback = new ConcurrentLinkedQueue<AsyncCallback<? extends Object>>(); 

    private volatile boolean shutdown = false; 

/** 
* The run method required by Runnable. It manages the asynchronous calls placed to this class. 
*/ 
@Override 
public final void run() { 
    test = new Object(); 
    while (!shutdown) { 
     if (!this.queue_methods.isEmpty()) { 
      String crtMethod = queue_methods.poll(); 
      Object[] crtParamArr = queue_params.poll(); 
      String methodName = crtMethod + "Async"; 

      Method method; 
      try { 
       method = this.getClass().getMethod(methodName); 
       try { 
        Object retVal = method.invoke(this, crtParamArr); 
        AsyncCallback<? extends Object> crtCallback = queue_callback.poll(); 
        crtCallback.returnValue(retVal); 
       } catch (Exception ex) {} 
       } catch (SecurityException ex) { 
       } catch (NoSuchMethodException ex) {} 
     } else { 
      try { 
       synchronized(test) { 
        test.wait(); 
       } 
      } catch (InterruptedException ex) { 
       System.out.println("READY"); 
      } catch (Exception ex) { 
       System.out.println("READY, but " + ex.getMessage()); 
      } 
     } 
    } 
} 

/** 
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters 
* @param methodName The name of the currently called method. methodName + "Async" is being called 
* @param parameters Parameters you may want to pass to the method 
*/ 
protected final void enqueueVoidCall(String methodName, Object... parameters) { 
    List<Object> tmpParam = new ArrayList<Object>(); 
    for (Object crt : parameters) { 
     tmpParam.add(crt); 
    } 
    queue_methods.add(methodName); 
    queue_params.add(parameters); 
    queue_callback.add(null); 
    test.notifyAll(); 
} 

/** 
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters 
* @param methodName The name of the currently called method. methodName + "Async" is being called 
* @param callBack An instance of AsyncCallback whose returnValue-method is called upon completion of the task. 
* @param parameters Parameters you may want to pass to the method 
*/ 
protected final void enqueueCall(String methodName, AsyncCallback<? extends Object> callBack, Object... parameters) { 
    List<Object> tmpParam = new ArrayList<Object>(); 
    for (Object crt : parameters) { 
     tmpParam.add(crt); 
    } 
    queue_methods.add(methodName); 
    queue_params.add(parameters); 
    queue_callback.add(callBack); 
    test.notifyAll(); 
} 

/** 
* Complete the currently running task, optionally return values and eventually shut down. The instance of this object becomes unusable after this call. 
*/ 
public void shutdown() { 
    shutdown=true; 
} 

} 

現在我有兩個類來測試的東西:

public class MySecondTask extends ThreadedClass { 
public void test1() { 
    this.enqueueVoidCall("test1", null); 
} 

public void test1Async() { 
    System.out.println("Start"); 
    try { 
     // do big job here 
    } catch (Exception ex) { } 
    System.out.println("Done"); 
} 
} 

和主方法開始的東西:

public class TestingClass { 
public static void main(String[] args) { 
    MySecondTask test = new MySecondTask(); 
    test.start(); 
    System.out.println("1. Thread [1]"); 
    // CORRECTION, SHOULD BE: 
    test.test1(); 
    // INSTEAD OF: 
    // test.test1Async(); 
    for(int q=0; q<=100000; q++) { 
     System.out.println("1:"+ new Date().getTime()+":"+ q); 
     if ((q % 1000) == 0) { 
      System.out.flush(); 
     } 
    } 
    System.err.println("1. Thread [2]"); 
} 

} 

不知何故,第二個線程的輸出總是首先(完全)出現,然後剩下的放在控制檯上。如果線程同時運行(這是預期的結果),控制檯輸出應該混合?!

任何想法是讚賞以及評論,以改善我的編碼風格。


編輯

引用的問題是相當解決。

現在我收到一個IllegalMonitorStateException,在我打電話的地方:ThreadedClass.notifyAll()。

也許我得到了一個錯誤的鎖。但是a)爲什麼需要在wait()方法中使用synchronized()以及如何使notifyAll() - 調用來取消阻塞wait()?


在此先感謝和問候

P.S:你們都在堆棧溢出做好。你已經幫助了我很多次而不知道它,謝謝你。保持!

+0

TestingClass.main()中是否有錯誤?我沒有看到它調用test1()的任何地方。 – 2009-07-27 21:14:52

+0

等一下,你從哪裏得到這個:AsyncCallback對象,它不是Java的一部分 – OscarRyz 2009-07-28 00:39:34

回答

5

這意味着,你可以調用 anyMethod(),它是由第二 線程執行其立即返回 和。

這聽起來很像與可調用,期貨和執行人的工作:

討厭打破它給你,但你可能真的想看看這個東西..

編輯解決以下

評論只是讓你在你的對象的方法是這樣的:

private ExecutorService executorService = Executors.newCachedThreadPool(); 

public Future<SomeObject> yourMethodName(String anyArguments) { 
    return executorService.submit(
     new Callable<SomeObject>() { 
      public SomeObject call() { 
       SomeObject obj = new SomeObject(); 
       /* Your Code Here*/; 
       return obj; 
      } 
     } 
    ); 
} 
+0

感謝您的提示。我已經聽說過他們,我想我知道這些設施是做什麼的。 我的問題是,我需要創建Runnable的另一個實現_each_我想要調用的類的方法。還是我錯了? 我只希望能夠調用myObject.method1(),也許以後myObject.method2()。這些需要立即返回並以異步方式進行後臺工作。我想用我的「呼叫隊列」嘗試,這個任務可以完成......?! – Atmocreations 2009-07-27 21:28:03

+0

看看上面的新代碼塊。這種類型的實現應該足以解決大多數問題。如果你想讓它變得更可怕/複雜,你可以添加反射來爲你添加Callables和threadpool提交,但我不會推薦它。 – Tim 2009-07-27 21:45:05

+0

謝謝。在我看來,這看起來更像我所做的那樣複雜。但我會仔細看看它是否可以幫助我。 – Atmocreations 2009-07-27 22:12:56

0

您的主線程等待test.test1Async()返回,您在那裏做了什麼?

0

Java線程在不同的機器上以不同的方式運行。有些機器搶先別人不是。如果第二個線程在第一個線程之前輸出其內容,那麼運行代碼的機器很可能是非搶先式的。如果您需要線程同步,則可以採用這種方式,但如果您不關心同步,則不能保證線程將如何運行。

另外,test.test1Async()正在第一個線程中運行,而不是第二個。 (這可能是什麼拿着東西)

+0

好吧,我可以理解第一點。 但它不應該在第一個線程中運行,因爲當前在run-method中的線程應該調用它(這是我的意圖)。 爲什麼從第一個線程調用它?這有什麼問題...... – Atmocreations 2009-07-27 21:23:05

+0

好的,能夠讀取StackOverflow並運行JVM的操作系統不是先發制人的多線程? – 2009-07-27 21:24:08

+0

@Amcreations - 當你告訴第二個線程啓動時,它開始在run方法中執行,但是run.方法中沒有調用test.test1Async(),它在主線程的main方法中調用。儘量不要啓動第二個線程,test1Async()仍然會運行。 – SquareRootOf2 2009-07-27 21:51:17

0

風格建議:看看java.lang.reflect.Proxy和InvocationHandler。 你可以實現一個InvocationHandler來避免你正在處理的反射內容,用戶可以直接調用真正的Interface方法。

0

您正在同步調用test1Async。如果你的bigjob在這裏完成,那麼它將不會與其他線程並行運行。你想要做的是這樣的:

public class TestingClass { 
public static void main(String[] args) 
{ 
    MySecondTask test = new MySecondTask(); 
    test.start(); 
    System.out.println("1. Thread [1]"); 
    //test.test1Async(); 
    for(int q=0; q<=100000; q++) 
    { 
      System.out.println("1:"+ new Date().getTime()+":"+ q); 
      if ((q % 1000) == 0) 
      { 
        System.out.flush(); 
      } 
    } 
    System.err.println("1. Thread [2]"); 
} 

} 

記住,你的ThreadedClass.run()方法會調用test1.testAsynch()爲您服務。我真的很驚訝,你沒有看到結果輸出兩次,或計算錯亂。

+0

看到了你的觀點,thx。現在又遇到了另一個問題...(你可能想看看我對奧斯卡雷耶斯的回答) – Atmocreations 2009-07-27 22:06:35

2

你永遠呼喚你的「螺紋」調度機制

我猜你嘗試調用(使用隊列等的):

test.test1(); 

這反過來又排隊調用test1Async,但錯誤你叫:

test.test1Async(); 

直接製作在單個線程整個執行。

替換:

.... 
    System.out.println("1. Thread [1]"); 
    test.test1Async(); 
    for(int q=0; q<=100000; q++) 
    { 
    ... 

有了:

.... 
    System.out.println("1. Thread [1]"); 
    test.test1(); 
    for (int q=0; q<=100000 ; q++) { 
    .... 

在編碼風格,pleeeeease在Java中(和JavaScript)在C#編碼時,使用左括號在同一行的聲明, C++和C就像你擁有的那樣更好。

還使用camelCase而不是separete_with_underscore。

下面是關於Java's coding conventions的更多文檔。

+0

哎喲,是的,你是對的。 (我剛剛發佈的評論去了哪裏?) 作爲darthcoder註釋掉了我看到的東西是不對的。 經過一些其他小的更正後,我又設法再次運行代碼。但這一次,它會在我調用notifyAll()的行上拋出IllegalMonitorStateException。當然,第二個線程(現在正在調用該方法)不是鎖擁有者,我知道這一點。但這就是通知的目的,對吧?繼續等待這個的其他線程。任何想法如何糾正它? 感謝 – Atmocreations 2009-07-27 22:05:01

+0

從JDK參考文獻:[拋出:IllegalMonitorStateException - 如果當前線程不是這個對象監視器的所有者]問題是,調用notifyAll的(線程)從未同步()的測試。 – 2009-07-27 22:53:55

+0

如果你沒有發佈stacktrace很難「猜測」,但我會猜測。在方法:enqueueCall()你調用test.notifyAll();爲了使用通知,所有線程必須擁有對象鎖。 – OscarRyz 2009-07-28 00:17:45

1

我建議你按照蒂姆在他的回答中所建議的方式來實現它。

你自己的想法,雖然有創意,打破了很多最佳做法,使你的代碼非常脆。只有很多事情要記住,否則它會微妙地破裂。

想想那些會追隨你的人,並且必須使用,維護和擴展你的代碼。想一想當你需要在一年內重新訪問這些代碼時會發生什麼。

只是你不應該做的東西的簡短列表:

  • 擴展Thread直接被認爲是不好的做法,寧願實現Runnable代替
  • 避免編碼方法文本 - 這將打破第一個重構
  • test1Async()應該是私有的,否則一個新的團隊將它稱爲直接
  • 方法的命名應該是明確的 - *異步()通常是指在做背景,而它實際上是周圍的其他方式
  • 整體脆弱 - 比方說,我需要改變test1()返回int而不是void - 我是否真的會記得更改其他方法?請問記得一年後再做嗎?
+0

THX您的答覆。 好吧,實現Runnable而不是延長線程是我可以輕鬆地生活的東西... ... Async()私有(或受保護的可擴展性)也是一個好點。因爲我現在並不在乎,因爲我只是想讓代碼按預期運行; o) 也許它很脆弱,我同意。但是當我按Tim的方式實現它時,我想我會再次遇到同樣的問題,記住更改代碼時不得不做的不同更改。 – Atmocreations 2009-07-28 07:22:00

+0

你有一個很好的積極態度,這是非常酷:)關於蒂姆的解決方案 - 優勢是它非常集中。例如,如果你想改變一些方法,你可以改變方法並改變它 - 就是這樣。不需要轉到另一個方法,也不需要更新某個文本字符串 - 只需找到方法並修復它。這現在看起來可能不重要,但在幾個月內你會感謝我:) – 2009-07-28 08:10:00

相關問題