multithreadedtc 是一個 java 庫用來測試并發應用。它的主要目的是為了解決并發應用的不确定的問題。你不能控制他們的執行順序。為了這個目睹,它包含了内部節拍器來控制應用的不同線程的執行順序。這些測試線程作為類的方法來實作的。
在這個指南,你将學習如何使用 multithreadedtc 庫來為linkedtransferqueue 實作一個測試。
準備
你必須從 http://code.google.com/p/ multithreadedtc/ 下載下傳 multithreadedtc library,并從 http://www.junit.org/ 下載下傳 junit library, version 4.10。把 junit-4.10.jar 和 multithreadedtc-1.01.jar檔案加入項目的庫中。
怎麼做呢…
按照這些步驟來實作下面的例子::
<code>01</code>
<code>//1. 建立一個類,名為 producerconsumertest,擴充 multithreadedtestcase 類。</code>
<code>02</code>
<code>public</code> <code>class</code> <code>producerconsumertest </code><code>extends</code> <code>multithreadedtestcase {</code>
<code>03</code>
<code>04</code>
<code>//2. 聲明一個私有 linkedtransferqueue 屬性,用 string 類為參數,名為 queue。</code>
<code>05</code>
<code>private</code> <code>linkedtransferqueue<string> queue;</code>
<code>06</code>
<code>07</code>
<code>//3. 實作 initialize() 方法。此方法不接收任何參數,也不傳回任何值。它調用父類的 initialize() 方法,然後初始化 queue 屬性。</code>
<code>08</code>
<code>@override</code>
<code>09</code>
<code>public</code> <code>void</code> <code>initialize() {</code>
<code>10</code>
<code> </code><code>super</code><code>.initialize();</code>
<code>11</code>
<code> </code><code>queue=</code><code>new</code> <code>linkedtransferqueue<string>();</code>
<code>12</code>
<code> </code><code>system.out.printf(</code><code>"test: the test has been initialized\n"</code><code>);</code>
<code>13</code>
<code>}</code>
<code>14</code>
<code>15</code>
<code>//4. 實作 thread1() 方法。它将實作的邏輯是第一個consumer。調用 queue 的 take() 方法,然後把傳回值寫入操控台。</code>
<code>16</code>
<code>public</code> <code>void</code> <code>thread1() </code><code>throws</code> <code>interruptedexception {</code>
<code>17</code>
<code> </code><code>string ret=queue.take();</code>
<code>18</code>
<code> </code><code>system.out.printf(</code><code>"thread 1: %s\n"</code><code>,ret);</code>
<code>19</code>
<code>20</code>
<code>21</code>
<code>//5. 實作 thread2() 方法。它将實作的邏輯是第二個consumer。首先,使用 waitfortick() 方法,一直等到第一個線程在 take() 方法中進入休眠。然後,調用queue的 take() 方法,并把傳回值寫入操控台。</code>
<code>22</code>
<code>public</code> <code>void</code> <code>thread2() </code><code>throws</code> <code>interruptedexception {</code>
<code>23</code>
<code> </code><code>waitfortick(</code><code>1</code><code>);</code>
<code>24</code>
<code>25</code>
<code> </code><code>system.out.printf(</code><code>"thread 2: %s\n"</code><code>,ret);</code>
<code>26</code>
<code>27</code>
<code>28</code>
<code>//6. 實作 thread3() 方法。它将實作的邏輯是producer。 首先,使用 waitfortick() 兩次一直等到2個consumers被阻塞。然後,調用 queue的 put() 方法插入2個string 到queue中。</code>
<code>29</code>
<code>public</code> <code>void</code> <code>thread3() {</code>
<code>30</code>
<code>31</code>
<code> </code><code>waitfortick(</code><code>2</code><code>);</code>
<code>32</code>
<code> </code><code>queue.put(</code><code>"event 1"</code><code>);</code>
<code>33</code>
<code> </code><code>queue.put(</code><code>"event 2"</code><code>);</code>
<code>34</code>
<code> </code><code>system.out.printf(</code><code>"thread 3: inserted two elements\n"</code><code>);</code>
<code>35</code>
<code>36</code>
<code>37</code>
<code>//7. 最後,實作 finish() 方法。寫資訊到操控台表明測試結束執行。使用assertequals() 方法檢查2個事件已經被consumed(queue的大小為0)。</code>
<code>38</code>
<code>public</code> <code>void</code> <code>finish() {</code>
<code>39</code>
<code> </code><code>super</code><code>.finish();</code>
<code>40</code>
<code> </code><code>system.out.printf(</code><code>"test: end\n"</code><code>);</code>
<code>41</code>
<code> </code><code>assertequals(</code><code>true</code><code>, queue.size()==</code><code>0</code><code>);</code>
<code>42</code>
<code> </code><code>system.out.printf(</code><code>"test: result: the queue is empty\n"</code><code>);</code>
<code>43</code>
<code>44</code>
<code>45</code>
<code>//8. 建立例子的主類通過建立一個類,名為 main 并添加 main()方法。</code>
<code>46</code>
<code>public</code> <code>class</code> <code>main {</code>
<code>47</code>
<code>48</code>
<code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) </code><code>throws</code> <code>throwable {</code>
<code>49</code>
<code>50</code>
<code>//9. 建立 producerconsumertest 對象,名為 test。</code>
<code>51</code>
<code>producerconsumertest test=</code><code>new</code> <code>producerconsumertest();</code>
<code>52</code>
<code>53</code>
<code>//10. 使用 testframework 類的 runonce()方法來執行測試。</code>
<code>54</code>
<code>system.out.printf(</code><code>"main: starting the test\n"</code><code>);</code>
<code>55</code>
<code>testframework.runonce(test);</code>
<code>56</code>
<code>system.out.printf(</code><code>"main: the test has finished\n"</code><code>);</code>
它是如何工作的…
在這個指南,你使用multithreadedtc庫為 linkedtransferqueue 類實作了一個測試。你可以使用這個庫和它的節拍器為任何并發應用或者類實作測試。在例子中,你實作經典的 producer/ consumer 問題,2個consumers 和一個producer。 你想要測試的是 在buffer裡的第一個介紹的 string 對象會被第一個consumer 消耗,和在buffer裡第二個介紹的string對象會被第二個到達的consumer消耗。
multithreadedtc庫是基于junit 庫的。junit庫是在java中最經常用來實作unit測試時使用的庫。使用 multithreadedtc 庫來實作一個基本測試,你必須擴充 multithreadedtestcase 類。這個類擴充了 junit.framework.assertjunit 類,包含了全部檢查測試結果的方法。它并沒有擴充 junit.framework.testcase 類,使用你不能在其他 junit 測試中導入multithreadedtc測試。
然後,你實作了以下這些方法:
initialize(): 此方法的實作是可選的。當你開始測試時,它就會執行,為了初始化測試中使用的對象而使用它。
finish(): 此方法的實作是可選的。當測試結束時,它就會執行。你可以使用它在測試或者檢查測試結果期間來關閉或者釋放資源。
實作測試的方法:這些方法的主要邏輯就是測試你的實作。他們用thread作為始關鍵詞連接配接着字元串 例如, thread1()。
使用 waitfortick() 方法來控制線程的執行順序。此方法接收一個整數type作為參數,把正在執行的thread放入休眠直到全部在測試裡運作的線程都被阻塞。等他們被阻塞時, multithreadedtc 庫調用 waitfortick() 方法 恢複被阻塞的線程。
傳遞給 waitfortick() 方法作為參數的整數是用來控制執行順序的。multithreadedtc 庫的節拍器有個内部計數器。當全部線程被阻塞時,庫增加計數器到下個在 waitfortick() 調用中的數字,那麼被阻塞的。
從内部來說,當 multithreadedtc 庫 執行測試,首先它執行 方法。然後,它建立每個用thread作為關鍵詞開頭的方法的線程(例子中是,方法 thread1(), thread2(), 和 thread3()),當全部線程都結束運作後,就執行 finish() 方法。為了運作測試,你要使用 testframework 類的 runonce() 方法。
更多…
如果 multithreadedtc library 檢測的全部線程都被阻塞,但是沒有一個是被 waitfortick() 方法阻塞的,那麼測試就會被認為在deadlock狀态,并抛出 java.lang.illegalstateexception 異常。
參見
第八章,測試并發應用:findbugs分析并發代碼