﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-细心!用心!耐心!-文章分类-多執行緒模式</title><link>http://www.blogjava.net/jesson2005/category/21672.html</link><description>吾非文人，乃市井一俗人也，读百卷书，跨江河千里，故申城一游； 
一两滴辛酸，三四年学业，五六点粗墨，七八笔买卖，九十道人情。</description><language>zh-cn</language><lastBuildDate>Wed, 18 Apr 2007 04:28:51 GMT</lastBuildDate><pubDate>Wed, 18 Apr 2007 04:28:51 GMT</pubDate><ttl>60</ttl><item><title>Design Pattern: Thread-Specific Storage 模式</title><link>http://www.blogjava.net/jesson2005/articles/111204.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111204.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111204.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111204.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111204.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111204.html</trackback:ping><description><![CDATA[無論如何，要編寫一個多執行緒安全（thread-safe）的程式總是困難的，為了使用的共用資源，您必須小心的對共用資源進行同步，同步帶來一定的效能延遲，而另一方面，在處理同步的時候，又要注意物件的鎖定與釋放，避免產生死結，種種因素都使得編寫多執行緒程式變得困難。<br><br>Thread-Specific Storage模式嘗試從另一個角度來解釋多執行緒共用資源的問題，其思考點很簡單，即然共用資源這麼困難，那麼就乾脆不要共用，何不為每個執行緒創造一個資源的複本，將每一個執行緒存取資料的行為加以隔離，其實現的方法，就是給予每一個執行緒一個特定空間來保管該執行緒所獨享的資源，也因此而稱之為 Thread- Specific Storage模式。<br><br>在Java中可以使用java.lang.ThreadLocal來實現這個模式，這個類別是從1.2之後開始提供，不過先來看看，如何自行實現一個簡單的ThreadLocal類別：<br>
<ul>
    <li>ThreadLocal.java </li>
</ul>
<pre>import java.util.*;<br><br>public class ThreadLocal {<br>    private Map storage = <br>                Collections.synchronizedMap(new HashMap());<br><br>    public Object get() {<br>        Thread current = Thread.currentThread();<br>        Object o = storage.get(current);<br><br>        if(o == null &amp;&amp; !storage.containsKey(current)) {<br>            o = initialValue();<br>            storage.put(current, o);<br>        }<br><br>        return o;<br>    }<br><br>    public void set(Object o) {<br>        storage.put(Thread.currentThread(), o);<br>    }<br><br>    public Object initialValue() {<br>        return null;<br>    }<br>} <br></pre>
<br>可以看到程式中使用執行緒本身作為key值，並將所獲得的資源放在Map物件中，如果第一次使用get()，也配置一個空間給執行緒，而 initialValue()可以用來設定什麼樣的初值要先儲存在這個空間中，在這邊先簡單的設定為null。<br><br>現在假設有一個原先在單執行緒環境下的資源SomeResource，現在考慮要該其在多執行緒環境下使用，若不想考慮複雜的執行緒共用互斥問題，此時可以使用ThreadLocal類別來使用SomeResource，例如：<br>
<ul>
    <li>Resource.java </li>
</ul>
<pre>public class Resource {<br>    private static final ThreadLocal threadLocal = <br>                                        new ThreadLocal();<br><br>    public static SomeResource getResource() {<br>        SomeResource resource =<br>                        (SomeResource) threadLocal.get();<br><br>        if(resource == null) {<br>            resource = new SomeResource();<br>            threadLocal.set(resource);<br>        }<br><br>        return resource;<br>    }<br>} <br></pre>
<br>上面所實作的ThreadLocal類別只是一個簡單的示範，您可以使用java.lang.ThreadLocal來實現Thread- Specific Storage模式，以獲得更好的效能，在這邊簡單的示範一個Log程式，它可以記錄每個執行緒的活動，所使用的是 java.util.logging中的類別：<br>
<ul>
    <li>SimpleThreadLogger.java </li>
</ul>
<pre>import java.io.*;<br>import java.util.logging.*;                            <br> <br>public class SimpleThreadLogger {<br>    private static final ThreadLocal threadLocal = <br>                                         new ThreadLocal();<br><br>    public static void log(String msg) {<br>        getThreadLogger().log(Level.INFO, msg);<br>    }<br><br>    private static Logger getThreadLogger() {<br>        Logger logger = (Logger) threadLocal.get();<br><br>        if(logger == null) {<br>            try {<br>                logger = Logger.getLogger(<br>                           Thread.currentThread().getName());<br>                // Logger 預設是在主控台輸出<br>                // 我們加入一個檔案輸出的Handler<br>                // 它會輸出XML的記錄文件<br>                logger.addHandler(<br>                    new FileHandler(<br>                           Thread.currentThread().getName() <br>                           + ".log"));<br>            }<br>            catch(IOException e) {}<br><br>            threadLocal.set(logger);<br>        }<br><br>        return logger;<br>    }<br>} <br></pre>
<br>可以使用下面這個程式來測試：<br>
<ul>
    <li>LoggerTest.java </li>
</ul>
<pre>public class LoggerTest {<br>    public static void main(String[] args) {<br>        new TestThread("thread1").start();<br>        new TestThread("thread2").start();<br>        new TestThread("thread3").start();<br>    }<br>}<br><br>class TestThread extends Thread {<br>    public TestThread(String name) {<br>        super(name);<br>    }<br><br>    public void run() {<br>        for(int i = 0; i &lt; 10; i++) {<br>            SimpleThreadLogger.log(getName() + <br>                                     ": message " + i);<br>            try {<br>                Thread.sleep(1000);<br>            }<br>            catch(Exception e) {<br>                SimpleThreadLogger.log(e.toString());<br>            }<br>        }<br>    }<br>} <br></pre>
<br>執行LoggerTest可以在主控台上看到輸出，並可以在同一目錄下找到三個log檔，分別記錄了三個執行緒的活動，透過 ThreadLocal，不用撰寫複雜的執行緒共用互斥邏輯。<br><br>Thread-Specific Storage模式的意義之一，就是「有時不共用是好的」，如果共用會產生危險，那就不要共用，當然，這種方式所犧牲掉的就是空間，您必須為每一個執行緒保留它們獨立的空間，這是一種以空間換取時間與安全性的方法。 
<img src ="http://www.blogjava.net/jesson2005/aggbug/111204.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:59 <a href="http://www.blogjava.net/jesson2005/articles/111204.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Read-Write-Lock 模式</title><link>http://www.blogjava.net/jesson2005/articles/111201.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:58:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111201.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111201.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111201.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111201.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111201.html</trackback:ping><description><![CDATA[如果有一個資料檔有可能同時間會有許多客戶端對它進行讀取與寫入的動作，則必須注意資料的同步問題，像是兩個寫入者進行寫入時，後一個寫入者的資料會有可能將次一個寫入者的資料覆蓋掉；而有時您希望讀取者看到的是最新的資料，如果在讀取的時候，有寫入者想要對資料進行寫入，則最好等待讀取者讀取完畢，相反的如果在寫入時有客戶想要讀取資料，則最好等待，以確保讀出來的資料是最新的資料。<br><br>讀取寫入的同步問題向來是難解的問題之一，有幾個可行的作法，例如若有寫入的動作時，則讀取者以唯讀模式開啟；或是如果有開啟資料檔的動作時，無論是讀取或是寫入，後一個開啟檔案的客戶都一律以唯讀模式開啟；還有最乾脆的作法，就是將這個問題由客戶決定，在開啟檔案時若已有其他人開啟中，則提供選項讓客戶決定要不要以唯讀模式開啟，通常這個作法是提供給檔案的擁有者使用。<br><br>Read-Write-Lock 模式提供給被讀取或寫入的資料「一把鎖」，在讀取或寫入時都必須先取得這把鎖，讀取的客戶可以同時共同這把鎖，而寫入的客戶也可以共用這把鎖，但讀取不可與寫入共用一把鎖，如果嘗試取得鎖時發現鎖已經被另一方取得，則等待直到鎖被釋放並重新取得它。<br><br>下圖讀取者讀取資料時的Sequence Diagram示例： <br>
<div style="TEXT-ALIGN: center"><img title=Read-Write-Lock style="WIDTH: 352px; HEIGHT: 413px" alt=Read-Write-Lock src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/readWriteLock-1.jpg"><br></div>
<br>現在假設讀取者已經取得鎖，而寫入者試圖進行寫入，它也試圖先取得鎖定，但發現鎖已經被讀取的一方擁有，於是先進入等待，直到讀取的一方解除鎖定為止：<br>
<div style="TEXT-ALIGN: center"><img title=Read-Write-Lock style="WIDTH: 442px; HEIGHT: 379px" alt=Read-Write-Lock src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/readWriteLock-2.jpg"><br></div>
<br>一個簡單的Java程式例子如下所示：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public void readData() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; lock.readLock();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; doRead();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; lock.readUnLock();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public void writeData() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; lock.writeLock();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; doWrite();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; lock.writeUnLock();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
&nbsp;<br><br>而最主要的關鍵還是在於鎖的實現，在Java中可以用wait()、notify()來實現，實現的片段如下：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;private boolean writerFirst = true; // 寫入優先</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public synchronized void readLock() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; try {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(writingWriters &gt; 0 || </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (writerFirst &amp;&amp; waitingWriters &gt; 0)) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; catch(InterruptedException) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; readingReaders++;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public synchronized void readUnLock() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; readingReaders--;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; writerFirst = true;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; notifyAll();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public synchronized void writeLock() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; waitingWriters++</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; try {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(readingReaders &gt; 0 || writingWriters &gt; 0) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; catch(InterruptedException) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; finally {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; waitingWriters--;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; writingWriters++;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public synchronized void writeUnLock() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; writingWriters--;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; writerFirst = false;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; notifyAll();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;} </span><br></div>
&nbsp; <br><br>其中writerFirst是寫入優先的旗標，它確保只要有寫入的執行緒在等待時，在解除鎖定的時候，可以優先由寫入執行緒取得鎖定，以確保讀取者讀取到的資料可以是最新的，但缺點就是寫入的動作很頻繁時，讀取者必須等待的機會將增多，相反的若設定為讀取優先，則讀取時的回應性會增高，但資料更新的速率將會下降，實際使用時要偏好哪一方，必須視應用的場合而定。
<img src ="http://www.blogjava.net/jesson2005/aggbug/111201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:58 <a href="http://www.blogjava.net/jesson2005/articles/111201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Two-phase Termination 模式</title><link>http://www.blogjava.net/jesson2005/articles/111202.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:58:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111202.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111202.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111202.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111202.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111202.html</trackback:ping><description><![CDATA[Two-phase Termination直譯的話是「兩相終止」，不過就這個模式而言，該譯作「兩階段終止」比較適當，想像您有一個執行緒正在週期性的運作，在「運作階段」您送出了停止執行緒的請求，這時候執行緒不該慌張的馬上終止目前的工作，而是先完成這一次週期的工作，然後進入「善後階段」完成一些善後的工作，例如關閉檔案或網路串流，所謂的兩階段終止，即中止「運作階段」，並完成「善後階段」，完整的完成執行緒的工作。<br><br>以Java的Thread終止而言，不建議您直接使用stop()方法來終止執行緒，stop()方法會丟出ThreadDeath例外強迫執行緒終止，即使執行緒正在運作階段或執行至synchronized區，如果您要終止執行緒，建議自行實作，例如：<br>
<div style="MARGIN-LEFT: 40px">&nbsp;<span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class SomeThread extends Thread {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private boolean isTerminated = false;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void terminate() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isTerminated = true;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!isTerminated) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ... some statements</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
&nbsp;<br>考慮到有時執行緒可能會執行至sleep()或wait()而進入Not Runnable狀態，使用上面的方法可能會延遲終止的請求，因而可以在要求終止時再呼叫interrupt()方法，這會丟出 InterruptedException，而使得執行緒從Not Runnable狀態中離開，因此可以改變一下程式：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class SomeThread extends Thread {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private boolean isTerminated = false;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void terminate() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isTerminated = true;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interrupt();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!isTerminated) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ... some statements</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException e) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
&nbsp;<br>在發出中止請求之後，如果執行緒是在Not Runnable狀態，會丟出InterruptedException，如果這個例外沒有先被捕捉，就會被run()中的catch InterruptedException捕捉，也就是說會直接離開while迴圈，因而如果您在發出終止請求後，要求先執行完這一個週期的工作，您要先捕捉這個例外，若不用完成這一個週期的工作，則不用捕捉這個例外，要如何作取決於您的程式。<br><br>如果執行緒要完成這一個週期的工作，在下一個週期開始之前檢查旗標，這時它的結果是false，所以離開while迴圈，這時候您可以進行一些善後工作，這個可以寫在finally區塊中，例如：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class SomeThread extends Thread {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private boolean isContinue = false;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void terminate() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isTerminated = true;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interrupt();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private void doWorkBeforeShutdown() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // .... do some work before shutdown</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!_isTerminated) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ... some statements</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException e) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doWorkBeforeShutdown();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
&nbsp;<br>上面這個程式大致上就是Two-phase Termination模式的架構，另外如果您的執行緒還服務著其它的物件，則在送出終止請求到完全終止之前，應該停止服務其它物件，您可以讓其它物件要求服務之前，先查詢執行緒是否已被要求終止，這可以藉由提供一個方法來達到：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class SomeThread extends Thread {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private boolean isTerminated = false;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void terminate() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isTerminated = true;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interrupt();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public boolean isTerminated() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return _isTerminated;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; private void doWorkBeforeShutdown() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // .... do some work before shutdown</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!_isTerminated) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ... some statements</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException e) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doWorkBeforeShutdown();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111202.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:58 <a href="http://www.blogjava.net/jesson2005/articles/111202.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Future 模式</title><link>http://www.blogjava.net/jesson2005/articles/111199.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111199.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111199.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111199.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111199.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111199.html</trackback:ping><description><![CDATA[Future模式可以簡單的看成是 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/ProxyPattern.htm">Proxy 模式</a> 與 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/ThreadPerMessage.htm">Thread-Per-Message 模式</a> 的結合，在Proxy模式中，用一個Proxy來代替真正的目標（Subject）生成，目標的生成可能是費時的，例如在開啟一個內嵌圖片的文件中，希望程式能儘快完成開啟文件的動作，並顯示一個可接受的畫面給使用者看，在還不需要看到圖片的頁面中先使用Proxy代替真正的圖片載入，只有在真正需要看到圖片時，才由Proxy物件載入真正的圖片。<br><br>考慮這樣一個情況，使用者可能快速翻頁瀏覽文件中，而圖片檔案很大，如此在瀏覽到有圖片的頁數時，就會導致圖片的載入，因而造成使用者瀏覽文件時會有停頓的現象，所以我們希望在文件開啟之後，仍有一個背景作業持續載入圖片，如此使用者在快速瀏覽頁面時，所造成的停頓可以獲得改善。<br><br>Future模式在請求發生時，會先產生一個Future物件給發出請求的客戶，它的作用就像是Proxy物件，而同時間，真正的目標物件之生成，由一個新的執行緒持續進行（即Thread-Per-Message），真正的目標物件生成之後，將之設定至Future之中，而當客戶端真正需要目標物件時，目標物件也已經準備好，可以讓客戶提取使用。 <br><br>
<div style="TEXT-ALIGN: center"><img title=Future style="WIDTH: 642px; HEIGHT: 545px" alt=Future src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/future-1.jpg"><br></div>
<br>一個簡單的Java程式片段示範可能像是這樣：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">....</span></div>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public Future request() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; final Future future = new Future();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; new Thread() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 下面這個動作可能是耗時的</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RealSubject subject = new RealSubject();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; future.setRealSubject(subject);</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }.start();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; return future;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111199.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:57 <a href="http://www.blogjava.net/jesson2005/articles/111199.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Producer Consumer 模式</title><link>http://www.blogjava.net/jesson2005/articles/111195.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:56:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111195.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111195.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111195.html</trackback:ping><description><![CDATA[Producer Consumer模式與 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GuardedSuspension.htm">Guarded Suspension 模式</a> 是類似的，只不過Guarded Suspension模式並不限制緩衝區的長度，Producer Consumer模式假設所生產的產品放置在一個長度有限制的緩衝區（就像是一個產品桌，它可以擺放的空間是有限的），如果緩衝區滿了，則生產者必須停止繼續將產品放到緩衝區中，直到消費者取走了產品而有了空間，而如果緩衝區中沒有產品，當然消費者必須等待，直到有新的產品放到緩衝區中。 <br><br>一個簡單的 UML 順序圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=ProducerConsumer style="WIDTH: 642px; HEIGHT: 526px" alt=ProducerConsumer src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/producerConsumer.jpg"><br></div>
簡單來說，Producer Consumer模式就像是加上了雙重防護與等待的Guarded Suspension模式，而它的兩個防護與等待的條件洽好相反，一個用Java實現的簡單流程架構如下：<br>
<ul>
    <li>ProductTable.java </li>
</ul>
<pre>import java.util.LinkedList;<br><br>public class ProductTable {<br>    private LinkedList products = new LinkedList();<br><br>    public synchronized void addProduct(Product product) {<br>        while(products.size() &gt;= 2) { // 容量限制為 2<br>            try {<br>                wait();<br>            }<br>            catch(InterruptedException e) {}<br>        }<br><br>        products.addLast(product);<br>        notifyAll();<br>    }<br>   <br>    public synchronized Product getProduct() {<br>        while(products.size() &lt;= 0) {<br>            try {<br>                wait();<br>            }<br>            catch(InterruptedException e) {}<br>        }<br><br>        Product product = (Product) products.removeFirst();<br>        notifyAll();<br>     <br>        return product;<br>    }<br>} </pre>
<br>以下舉一個最簡單的：生產者每次生產一個整數並放置在桌子上，而消費者消耗整數，桌子上一次只能放置一個整數，如果桌子上已有整數，則生產者等待消費者將整數消耗並通知生產者生產下一個整數，如果桌子上沒有整數，則消費者等待生產者生產整數並通知消費者可以消耗整數。<br>
<ul>
    <li>Producer.java </li>
</ul>
<pre>public class Producer extends Thread {<br>    private ProductTable productTable;<br>   <br>    public Producer(ProductTable productTable) {<br>        this.productTable = productTable;<br>    }<br>   <br>    public void run() {<br>        System.out.println("Produce integer......");<br>        for(int product = 1; product &lt;= 10; product++) {<br>            try {<br>                // wait for a random time<br>                Thread.sleep((int) Math.random() * 3000);<br>            }<br>            catch(InterruptedException e) {<br>                e.printStackTrace();<br>            }<br>            productTable.setIntProduct(product);<br>        }      <br>    }<br>} <br></pre>
<br>
<ul>
    <li>Consumer.java </li>
</ul>
<pre>public class Consumer extends Thread {<br>    private ProductTable productTable;<br>   <br>    public Consumer(ProductTable productTable) {<br>        this.productTable = productTable;<br>    }<br>   <br>    public void run() {<br>        for(int i = 1; i &lt;= 10; i++) {<br>            try {<br>                // wait for a random time<br>                Thread.sleep((int) (Math.random() * 3000));<br>            }<br>            catch(InterruptedException e) {<br>                e.printStackTrace();<br>            }<br>            productTable.getProductInt();<br>        }<br>    }<br>} <br></pre>
<p>&#160;</p>
生產者將產品放至桌上，而消費者將產品從桌上取走，所以桌子是個維護是否讓被放置或消耗產品的地方，由它來決定誰必須等待與通知：
<ul>
    <li>ProductTable.java </li>
</ul>
<pre>public class ProductTable {<br>    private int productInt = -1; // -1 for no product<br><br>    public synchronized void setIntProduct(int product) {<br>        if(productInt != -1) {<br>            try {<br>                wait();<br>            }<br>            catch(InterruptedException e) {<br>                e.printStackTrace();<br>            }<br>        }<br><br>        productInt = product;<br>        System.out.println("set (" + product + ")");<br>        notify();<br>    }<br>   <br>    public synchronized int getProductInt() {<br>        if(productInt == -1) {<br>            try {<br>                wait();<br>            }<br>            catch(InterruptedException e) {<br>                e.printStackTrace();<br>            }<br>        }<br><br>        int p = productInt;<br>        System.out.println("Get (" + productInt + ")");<br>        productInt = -1;<br>              <br>        notify();<br>      <br>        return p;<br>    }<br>} <br></pre>
<br>生產者會生產10個整數，而消費者會消耗10個整數，由於桌上只能放置一個整數，所以每生產一個就消耗一個。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:56 <a href="http://www.blogjava.net/jesson2005/articles/111195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Worker Thread 模式</title><link>http://www.blogjava.net/jesson2005/articles/111196.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:56:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111196.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111196.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111196.html</trackback:ping><description><![CDATA[Worker Thread模式在Request的管理上像是 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/ProducerConsumer.htm">Producer Consumer 模式</a>，在Request的行為上像是 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/CommandPattern.htm">Command 模式</a>。<br><br>Producer Consumer模式專注於Product的生產與消費，至於Product被消費時是作何處理，則不在它的討論範圍之中。 <br>
<div style="TEXT-ALIGN: center"><img title=WorkerThread style="WIDTH: 489px; HEIGHT: 184px" alt=WorkerThread src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/workerThread-1.jpg"><br></div>
<br>如果您的Product是一個Request，消費者取得Request之後，執行Request中指定的請求方法，也就是使用Command模式，並且您的Request緩衝區還管理了Consumer，就有Worker Thread模式的意思了。<br>
<div style="TEXT-ALIGN: center"><img title=WorkerThread style="WIDTH: 456px; HEIGHT: 244px" alt=WorkerThread src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/workerThread-2.jpg"><br></div>
<br>在Sequence Diagram上，可以看出Worker Thread同時展現了Producer Consumer模式與Command模式：<br>
<div style="TEXT-ALIGN: center"><img title=WorkThread style="WIDTH: 491px; HEIGHT: 400px" alt=WorkThread src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/workerThread-3.jpg"></div>
利用Java實現的一個Channel類如下所示：<br>
<ul>
    <li>Channel.java </li>
</ul>
<pre>import java.util.LinkedList; <br><br>public class Channel { <br>    private LinkedList requests; <br>    private WorkerThread[] workerThreads; <br><br>    public Channel(int threadNumber) { <br>        requests = new LinkedList(); <br>        workerThreads = new WorkerThread[threadNumber]; <br>        for(int i = 0; i &lt; workerThreads.size(); i++) { <br>            workerThreads[i] = new WorkerThread(); <br>           workerThreads[i].start(); <br>        } <br>    } <br><br>    public synchronized void putRequest(Request request) { <br>        while(requests.size() &gt;= 2) { // 容量限制為 2 <br>            try { <br>                wait(); <br>            } <br>            catch(InterruptedException e) {} <br>        } <br><br>        requests.addLast(request); <br>        notifyAll(); <br>    } <br>    <br>    public synchronized Request getProduct() { <br>        while(requests.size() &lt;= 0) { <br>            try { <br>                wait(); <br>            } <br>            catch(InterruptedException e) {} <br>        } <br><br>        Request request = (Request) requests.removeFirst(); <br>        notifyAll(); <br>      <br>        return request;<br>    } <br>} <br></pre>
<br>Request類與WorkerThread類之間採的Command模式：<br>
<ul>
    <li>Request.java </li>
</ul>
<pre>public class Request() { <br>    // .... <br><br>    public void execute() { <br>        // do some work.... <br>    } <br>} <br></pre>
<br>
<ul>
    <li>WorkerThread.java </li>
</ul>
<pre>public class WorkerThread extends Thread { <br>    // ... <br><br>    public void run() { <br>        while(true) { <br>            Request request = channel.getRequest(); <br>            request.execute(); <br>        } <br>    } <br>} <br></pre>
<br>就行為上，WorkerThread就是有請求來了就作，如果沒有請求，則所有的WorkerThread就等待，直到有新的工作進來而通知它們，取得請求的WorkerThread要作的工作，就直接定義在execute()中。 <br><br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:56 <a href="http://www.blogjava.net/jesson2005/articles/111196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Thread-Per-Message 模式</title><link>http://www.blogjava.net/jesson2005/articles/111198.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:56:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111198.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111198.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111198.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111198.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111198.html</trackback:ping><description><![CDATA[Thread-Per-Message模式是一個很簡單但很常應用的模式，尤其是在GUI程式中，我們舉個例子，當您設計一個文件編輯器時，您可能像這樣註冊一個開啟檔案的事件處理：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;menuOpenFile.addActionListener(</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; new ActionListener() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed(ActionEvent e) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; openFile();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;);</span><br></div>
&nbsp;<br><br>openFile()方法中主要是開啟檔案、一行一行讀檔案文字並設定文字至文字區域中，這樣設計基本上沒有什麼問題，例如果您的文件內容很長，在讀檔必須花費一些時間時，您會發現在檔案讀取完畢前，您的視窗會有明顯的停頓現象。<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;menuOpenFile.addActionListener(</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; new ActionListener() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed(ActionEvent e) {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Thread(new Runnable(){</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; openFile();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }).start();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;);</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br></div>
在事件發生之後，您將檔案處理的動作交由一個執行緒去執行，而事件處理執行緒直接回到回應事件的狀態，如此即可解決視窗在載入檔案時的停頓。<br><br>簡單的說，Thread-Per-Message模式是在某個請求發生時，新增一個執行緒來執行該請求，而主執行緒繼續往下執行，除了上面的載入檔案例子之外，像是進行搜尋、字串轉換之類需要一些時間來執行的工作時，使用Thread-Per-Message模式都可以提高主執行緒（界面）的回應性。<br><br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111198.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:56 <a href="http://www.blogjava.net/jesson2005/articles/111198.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Guarded Suspension 模式</title><link>http://www.blogjava.net/jesson2005/articles/111194.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:54:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111194.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111194.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111194.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111194.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111194.html</trackback:ping><description><![CDATA[考慮這麼一個伺服器，它可以處理來自多個客戶端的服務請求（Request），為了不丟失客戶的請求，它要維持一個緩衝區，客戶的請求會先儲存至緩衝區中，而伺服器會從緩衝區中取出請求並執行，如果緩衝區中沒有請求，則伺服器就等待，直到被通知有新的請求存入緩衝區中，伺服器再度進行請求的執行。<br><br>關於這個描述的一個簡單 UML 順序圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=GuardedSuspension style="WIDTH: 542px; HEIGHT: 369px" alt=GuardedSuspension src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/guardedSuspension.jpg"><br></div>
<br>首先要考慮到，緩衝區會同時被兩個以上的執行緒進行存取，即伺服器的請求處理執行緒與客戶端執行緒，所以必須對緩衝區進行防護。<br><br>再來是當緩衝區中沒有請求時，伺服器必須等待直到被通知有新的請求。<br><br>Guarded Suspension模式關注的是執行的流程架構，以Java來實現這個架構的話如下所示： <br>
<ul>
    <li>RequestQueue.java </li>
</ul>
<pre>public class RequestQueue {<br>    private java.util.LinkedList queue;<br>    public RequestQueue() {<br>        queue = new java.util.LinkedList();<br>    }<br><br>    public synchronized Request getRequest() {<br>        while(queue.size() &lt;= 0) {<br>            try {<br>                wait();<br>            }<br>            catch(InterruptedException e) {}<br>        }<br>        return (Request) queue.removeFirst();<br>    }<br><br>    public synchronized void putRequest(Request request) {<br>        queue.addLast(request);<br>        notifyAll();<br>    }<br>} <br></pre>
<br>一個例子是多人聊天伺服器，請求可能只是一個客戶端送出的聊天訊息，聊天訊息會先存至緩衝區中，伺服器會不斷的從緩衝區中取出聊天訊息並發給客戶端，如果緩衝區中沒有新訊息，則伺服器就進入等待，直到有一個客戶端發出聊天訊息並存入緩衝區中，此時伺服器再度被通知，然後再度取出訊息並進行發送。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111194.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:54 <a href="http://www.blogjava.net/jesson2005/articles/111194.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>