天天看點

線程同步工具(二)控制并發通路多個資源

在并發通路資源的控制中,你學習了信号量(semaphores)的基本知識。

在上個指南,你實作了使用binary semaphores的例子。那種semaphores是用來保護通路一個共享資源的,或者說一個代碼片段每次隻能被一個線程執行。但是semaphores也可以用來保護多個資源的副本,也就是說當你有一個代碼片段每次可以被多個線程執行。

在這個指南中,你将學習怎樣使用semaphore來保護多個資源副本。你将實作的例子會有一個print queue但可以在3個不同的列印機上列印檔案。

準備

指南中的例子是使用 Eclipse IDE 來實作的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 打開并建立一個新的java任務。實作在控制并發通路資源裡描述的例子。

怎麼做呢<b>…</b>

按照這些步驟來實作下面的例子:

<code>01</code>

<code>//1.  如我們之前提到的,你将實作semaphores來修改print queue例子。打開PrintQueue類并聲明一個boolean array名為 freePrinters。這個array儲存空閑的等待列印任務的和正在列印文檔的printers。</code>

<code>02</code>

<code>private</code> <code>boolean</code> <code>freePrinters[];</code>

<code>03</code>

<code>04</code>

<code>//2.   接着,聲明一個名為lockPrinters的Lock對象。将要使用這個對象來保護freePrinters array的通路。</code>

<code>05</code>

<code>private</code> <code>Lock lockPrinters;</code>

<code>06</code>

<code>07</code>

<code>//3.   修改類的構造函數并初始化新聲明的對象們。freePrinters array 有3個元素,全部初始為真值。semaphore用3作為它的初始值。</code>

<code>08</code>

<code>public</code> <code>PrintQueue(){</code>

<code>09</code>

<code>10</code>

<code>semaphore=</code><code>new</code> <code>Semaphore(</code><code>3</code><code>);</code>

<code>11</code>

<code>freePrinters=</code><code>new</code> <code>boolean</code><code>[</code><code>3</code><code>];</code>

<code>12</code>

<code>13</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;</code><code>3</code><code>; i++){</code>

<code>14</code>

<code>    </code><code>freePrinters[i]=</code><code>true</code><code>;</code>

<code>15</code>

<code>}</code>

<code>16</code>

<code>lockPrinters=</code><code>new</code> <code>ReentrantLock();</code>

<code>17</code>

<code>18</code>

<code>19</code>

<code>//4.   修改printJob()方法。它接收一個稱為document的對象最為唯一參數。</code>

<code>20</code>

<code>public</code> <code>void</code> <code>printJob (Object document){</code>

<code>21</code>

<code>22</code>

<code>//5.   首先,調用acquire()方法獲得semaphore的通路。由于此方法會抛出 InterruptedException異常,是以必須加入處理它的代碼。</code>

<code>23</code>

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

<code>24</code>

<code>    </code><code>semaphore.acquire();</code>

<code>25</code>

<code>26</code>

<code>//6.   接着使用私有方法 getPrinter()來獲得被安排列印任務的列印機的号碼。</code>

<code>27</code>

<code>int</code> <code>assignedPrinter=getPrinter();</code>

<code>28</code>

<code>29</code>

<code>//7.    然後, 随機等待一段時間來實作模拟列印文檔的行。</code>

<code>30</code>

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

<code>31</code>

<code>System.out.printf(</code><code>"%s: PrintQueue: Printing a Job in Printer%d during %d seconds\n"</code><code>,Thread.currentThread().getName(), assignedPrinter,duration);</code>

<code>32</code>

<code>TimeUnit.SECONDS.sleep(duration);</code>

<code>33</code>

<code>34</code>

<code>//8.   最後,調用release() 方法來解放semaphore并标記列印機為空閑,通過在對應的freePrinters array引索内配置設定真值。</code>

<code>35</code>

<code>freePrinters[assignedPrinter]=</code><code>true</code><code>;</code>

<code>36</code>

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

<code>37</code>

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

<code>38</code>

<code>}</code><code>finally</code> <code>{</code>

<code>39</code>

<code>    </code><code>semaphore.release();</code>

<code>40</code>

<code>41</code>

<code>42</code>

<code>//9.  實作 getPrinter() 方法。它是一個私有方法,傳回一個int值,并不接收任何參數。</code>

<code>43</code>

<code>private</code> <code>int</code> <code>getPrinter() {</code>

<code>44</code>

<code>45</code>

<code>//10. 首先,聲明一個int變量來儲存printer的引索值。</code>

<code>46</code>

<code>int</code> <code>ret=-</code><code>1</code><code>;</code>

<code>47</code>

<code>48</code>

<code>//11. 然後, 獲得lockPrinters對象 object的通路。</code>

<code>49</code>

<code>50</code>

<code>lockPrinters.lock();</code>

<code>51</code>

<code>52</code>

<code>//12. 然後,在freePrinters array内找到第一個真值并在一個變量中儲存這個引索值。修改值為false,因為等會這個列印機就會被使用。</code>

<code>53</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;freePrinters.length; i++) {</code>

<code>54</code>

<code>if</code> <code>(freePrinters[i]){</code>

<code>55</code>

<code>    </code><code>ret=i;</code>

<code>56</code>

<code>    </code><code>freePrinters[i]=</code><code>false</code><code>;</code>

<code>57</code>

<code>    </code><code>break</code><code>;</code>

<code>58</code>

<code>59</code>

<code>60</code>

<code>61</code>

<code>//13. 最後,解放lockPrinters對象并傳回引索對象為真值。</code>

<code>62</code>

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

<code>63</code>

<code>64</code>

<code>65</code>

<code>    </code><code>lockPrinters.unlock();</code>

<code>66</code>

<code>67</code>

<code>return</code> <code>ret;</code>

<code>68</code>

<code>69</code>

<code>//14. Job 和 Core 類不做任何改變。</code>

它是怎麼工作的…

在例子中的PrintQueue類的關鍵是:Semaphore對象建立的構造方法是使用3作為參數的。這個例子中,前3個調用acquire() 方法的線程會獲得臨界區的通路權,其餘的都會被阻塞 。當一個線程結束臨界區的通路并解放semaphore時,另外的線程才可能獲得通路權。

在這個臨界區,線程獲得被配置設定列印的列印機的引索值。例子的這部分讓例子更真實,而且它沒有使用任何與semaphores相關的代碼。以下的裁圖展示了這個例子的執行輸出:

線程同步工具(二)控制并發通路多個資源

每個文檔都被安排到第一個空閑的列印機列印。

更多…

The acquire(), acquireUninterruptibly(), tryAcquire(),和release()方法有一個外加的包含一個int參數的版本。這個參數表示 線程想要擷取或者釋放semaphore的許可數。也可以這樣說,這個線程想要删除或者添加到semaphore的内部計數器的機關數量。在這個例子中acquire(), acquireUninterruptibly(), 和tryAcquire() 方法, 如果計數器的值小于許可值,那麼線程就會被阻塞直到計數器到達或者大于許可值。

繼續閱讀