在并發通路資源的控制中,你學習了信号量(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() 方法, 如果計數器的值小于許可值,那麼線程就會被阻塞直到計數器到達或者大于許可值。
參見
<a href="http://ifeve.com/thread-synchronization-utilities-1/">第三章,線程同步應用:并發地通路資源的控制</a>
第八章,同步應用的測試:修改lock接口
<a href="http://ifeve.com/basic-thread-synchronization-7/">第二章,基本線程同步:修改lock的公平性</a>