天天看點

線程執行者(五)運作多個任務并處理第一個結果

運作多個任務并處理第一個結果

在并發程式設計中的一個常見的問題就是,當有多種并發任務解決一個問題時,你隻對這些任務的第一個結果感興趣。比如,你想要排序一個數組。你有多種排序算法。 你可以全部啟用它們,并且擷取第一個結果(對于給定數組排序最快的算法的結果)。

在這個指南中,你将學習如何使用threadpoolexecutor類的場景。你将繼續實作一個示例,一個使用者可以被兩種機制驗證。如果使用其中一個機制驗證通過,使用者将被确認驗證通過。

準備工作…

這個指南的例子使用eclipse ide實作。如果你使用eclipse或其他ide,如netbeans,打開它并建立一個新的java項目。

如何做…

按以下步驟來實作的這個例子:

1.建立uservalidator類,實作使用者驗證過程。

<code>1</code>

<code>public</code> <code>class</code> <code>uservalidator {</code>

2.聲明一個私有的、類型為string、名為name的屬性,用來存儲系統驗證使用者的名稱。

<code>private</code> <code>string name;</code>

3.實作uservalidator類的構造器,初始化這個屬性。

<code>public</code> <code>uservalidator(string name) {</code>

<code>2</code>

<code>this</code><code>.name=name;</code>

<code>3</code>

<code>}</code>

4.實作validate()方法。接收你想要驗證使用者的兩個string類型參數,一個為name,一個為password。

<code>public</code> <code>boolean</code> <code>validate(string name, string password) {</code>

5.建立random對象,名為random。

<code>random random=</code><code>new</code> <code>random();</code>

6.等待個随機時間,用來模拟使用者驗證的過程。

<code>try</code> <code>{</code>

<code>long</code> <code>duration=(</code><code>long</code><code>)(math.random()*</code><code>10</code><code>);</code>

<code>system.out.printf(</code><code>"validator %s: validating a user during %d seconds\n"</code><code>,</code><code>this</code><code>.name,duration);</code>

<code>4</code>

<code>timeunit.seconds.sleep(duration);</code>

<code>5</code>

<code>}</code><code>catch</code> <code>(interruptedexception e) {</code>

<code>6</code>

<code>return</code> <code>false</code><code>;</code>

<code>7</code>

7.傳回一個随機boolean值。如果使用者驗證通過,這個方法将傳回true,否則,傳回false。

<code>return</code> <code>random.nextboolean();</code>

8.實作getname()方法,傳回name屬性值。

<code>public</code> <code>string getname(){</code>

<code>return</code> <code>name;</code>

9.現在,建立taskvalidator類,用來執行uservalidation對象作為并發任務的驗證過程。指定它實作callable接口,并參數化為string類型。

<code>public</code> <code>class</code> <code>taskvalidator</code><code>implements</code> <code>callable&lt;string&gt; {</code>

10.聲明一個私有的、類型為uservalidator、名為validator的屬性。

<code>private</code> <code>uservalidator validator;</code>

11.聲明兩個私有的、類型為string、名分别為user和password的屬性。

<code>private</code> <code>string user;</code>

<code>private</code> <code>string password;</code>

12.實作taskvalidator類,初始化這些屬性。

<code>public</code> <code>taskvalidator(uservalidator validator, string user,</code>

<code>string password){</code>

<code>this</code><code>.validator=validator;</code>

<code>this</code><code>.user=user;</code>

<code>this</code><code>.password=password;</code>

13.實作call()方法,傳回一個string類型對象。

<code>@override</code>

<code>public</code> <code>string call()</code><code>throws</code> <code>exception {</code>

14.如果使用者沒有通過uservalidator對象驗證,寫入一條資訊到控制台,表明這種情況,并且抛出一個exception異常。

<code>if</code> <code>(!validator.validate(user, password)) {</code>

<code>system.out.printf(</code><code>"%s: the user has not been found\n"</code><code>,validator.getname());</code>

<code>throw</code> <code>new</code> <code>exception(</code><code>"error validating user"</code><code>);</code>

15.否則,寫入一條資訊到控制台表明使用者已認證驗證,并傳回uservalidator對象的名稱。

<code>system.out.printf(</code><code>"%s: the user has been found\n"</code><code>,validator.getname());</code>

<code>return</code> <code>validator.getname();</code>

16.現在,實作這個示例的主類,建立main類,實作main()方法。

<code>public</code> <code>class</code> <code>main {</code>

<code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>

17.建立兩個string對象,一個名為name,另一個名為password,使用”test”值初始化它們。

<code>string username=</code><code>"test"</code><code>;</code>

<code>string password=</code><code>"test"</code><code>;</code>

18.建立兩個uservalidator對象,一個名為ldapvalidator,另一個名為dbvalidator。

<code>uservalidator ldapvalidator=</code><code>new</code> <code>uservalidator(</code><code>"ldap"</code><code>);</code>

<code>uservalidator dbvalidator=</code><code>new</code> <code>uservalidator(</code><code>"database"</code><code>);</code>

19.建立兩個taskvalidator對象,分别為ldaptask和dbtask。分别使用ldapvalidator和dbvalidator初始化它們。

<code>taskvalidator ldaptask=</code><code>new</code> <code>taskvalidator(ldapvalidator,username, password);</code>

<code>taskvalidator dbtask=</code><code>new</code> <code>taskvalidator(dbvalidator,username,password);</code>

20.建立taskvalidator隊列,添加兩個已建立的對象(ldaptask和dbtask)。

<code>list&lt;taskvalidator&gt; tasklist=</code><code>new</code> <code>arraylist&lt;&gt;();</code>

<code>tasklist.add(ldaptask);</code>

<code>tasklist.add(dbtask);</code>

21.使用executors類的newcachedthreadpool()方法建立一個新的threadpoolexecutor對象和一個類型為string,名為result的變量。

<code>executorservice executor=(executorservice)executors.newcachedthreadpool();</code>

<code>string result;</code>

22.調用executor對象的invokeany()方法。該方法接收tasklist參數,傳回string類型。同樣,它将該方法傳回的string對象寫入到控制台。

<code>result = executor.invokeany(tasklist);</code>

<code>system.out.printf(</code><code>"main: result: %s\n"</code><code>,result);</code>

<code>e.printstacktrace();</code>

<code>}</code><code>catch</code> <code>(executionexception e) {</code>

<code>8</code>

23.使用shutdown()方法結束執行者,寫入一條資訊到控制台,表明程式已結束。

<code>executor.shutdown();</code>

<code>system.out.printf(</code><code>"main: end of the execution\n"</code><code>);</code>

它是如何工作的…

main 類是這個示例的關鍵。threadpoolexecutor類中的invokeany()方法接收任務數列,并啟動它們,傳回完成時沒有抛出異常的第一個 任務的結果。該方法傳回的資料類型與啟動任務的call()方法傳回的類型一樣。在本例中,它傳回string值。

以下截圖顯示,當一個任務驗證使用者時,執行示例的部分輸出:

線程執行者(五)運作多個任務并處理第一個結果

這 個示例有兩個傳回随機boolean值的uservalidator對象。每個uservalidator對象被一個實作taskvalidator類的callable對象使用。如果uservalidator類的validate()方法傳回false,taskvalidator類将抛出異常。否則,它将傳回true值。

是以,我們有兩個任務,可以傳回true值或抛出異常。有以下4種情況:

兩個任務都傳回ture。invokeany()方法的結果是第一個完成任務的名稱。

第一個任務傳回true,第二個任務抛出異常。invokeany()方法的結果是第一個任務的名稱。

第一個任務抛出異常,第二個任務傳回true。invokeany()方法的結果是第二個任務的名稱。

兩個任務都抛出異常。在本例中,invokeany()方法抛出一個executionexception異常。

如果你多次運作這個示例,你可以擷取以上這4種情況。

以下截圖顯示當兩個任務抛出異常時,應用程式的輸出:

線程執行者(五)運作多個任務并處理第一個結果

不止這些…

threadpoolexecutor類提供其他版本的invokeany()方法:

invokeany(collection&lt;? extends callable&lt;t&gt;&gt; tasks, long timeout,timeunit unit):此方法執行所有任務,并傳回第一個完成(未逾時)且沒有抛出異常的任務的結果。timeunit類是個枚舉類,有如下常量:days,hours,microseconds,milliseconds, minutes,,nanoseconds 和seconds。

參見

在第4章,線程執行者中的運作多個任務并處理所有結果指南