在并發通路資源的控制中,你學習了信号量(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<</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<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() 方法, 如果計數器的值小于許可值,那麼線程就會被阻塞直到計數器到達或者大于許可值。