天天看點

測試并發應用(九)MultithreadedTC測試并發代碼

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&lt;string&gt; 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&lt;string&gt;();</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分析并發代碼