keep moving!

We must not cease from exploration. And the end of all our exploring will be to arrive where we began and to know the place for the first time.
随笔 - 37, 文章 - 2, 评论 - 3, 引用 - 0
数据加载中……

Design Pattern: Thread-Specific Storage 模式

無論如何,要編寫一個多執行緒安全(thread-safe)的程式總是困難的,為了使用的共用資源,您必須小心的對共用資源進行同步,同步帶來一定的效能延遲,而另一方面,在處理同步的時候,又要注意物件的鎖定與釋放,避免產生死結,種種因素都使得編寫多執行緒程式變得困難。

Thread-Specific Storage模式嘗試從另一個角度來解釋多執行緒共用資源的問題,其思考點很簡單,即然共用資源這麼困難,那麼就乾脆不要共用,何不為每個執行緒創造一個資源的複本,將每一個執行緒存取資料的行為加以隔離,其實現的方法,就是給予每一個執行緒一個特定空間來保管該執行緒所獨享的資源,也因此而稱之為 Thread- Specific Storage模式。

在Java中可以使用java.lang.ThreadLocal來實現這個模式,這個類別是從1.2之後開始提供,不過先來看看,如何自行實現一個簡單的ThreadLocal類別:
  • ThreadLocal.java
import java.util.*;

public class ThreadLocal {
private Map storage =
Collections.synchronizedMap(new HashMap());

public Object get() {
Thread current = Thread.currentThread();
Object o = storage.get(current);

if(o == null && !storage.containsKey(current)) {
o = initialValue();
storage.put(current, o);
}

return o;
}

public void set(Object o) {
storage.put(Thread.currentThread(), o);
}

public Object initialValue() {
return null;
}
}

可以看到程式中使用執行緒本身作為key值,並將所獲得的資源放在Map物件中,如果第一次使用get(),也配置一個空間給執行緒,而 initialValue()可以用來設定什麼樣的初值要先儲存在這個空間中,在這邊先簡單的設定為null。

現在假設有一個原先在單執行緒環境下的資源SomeResource,現在考慮要該其在多執行緒環境下使用,若不想考慮複雜的執行緒共用互斥問題,此時可以使用ThreadLocal類別來使用SomeResource,例如:
  • Resource.java
public class Resource {
private static final ThreadLocal threadLocal =
new ThreadLocal();

public static SomeResource getResource() {
SomeResource resource =
(SomeResource) threadLocal.get();

if(resource == null) {
resource = new SomeResource();
threadLocal.set(resource);
}

return resource;
}
}

上面所實作的ThreadLocal類別只是一個簡單的示範,您可以使用java.lang.ThreadLocal來實現Thread- Specific Storage模式,以獲得更好的效能,在這邊簡單的示範一個Log程式,它可以記錄每個執行緒的活動,所使用的是 java.util.logging中的類別:
  • SimpleThreadLogger.java
import java.io.*;
import java.util.logging.*;

public class SimpleThreadLogger {
private static final ThreadLocal threadLocal =
new ThreadLocal();

public static void log(String msg) {
getThreadLogger().log(Level.INFO, msg);
}

private static Logger getThreadLogger() {
Logger logger = (Logger) threadLocal.get();

if(logger == null) {
try {
logger = Logger.getLogger(
Thread.currentThread().getName());
// Logger 預設是在主控台輸出
// 我們加入一個檔案輸出的Handler
// 它會輸出XML的記錄文件
logger.addHandler(
new FileHandler(
Thread.currentThread().getName()
+ ".log"));
}
catch(IOException e) {}

threadLocal.set(logger);
}

return logger;
}
}

可以使用下面這個程式來測試:
  • LoggerTest.java
public class LoggerTest {
public static void main(String[] args) {
new TestThread("thread1").start();
new TestThread("thread2").start();
new TestThread("thread3").start();
}
}

class TestThread extends Thread {
public TestThread(String name) {
super(name);
}

public void run() {
for(int i = 0; i < 10; i++) {
SimpleThreadLogger.log(getName() +
": message " + i);
try {
Thread.sleep(1000);
}
catch(Exception e) {
SimpleThreadLogger.log(e.toString());
}
}
}
}

執行LoggerTest可以在主控台上看到輸出,並可以在同一目錄下找到三個log檔,分別記錄了三個執行緒的活動,透過 ThreadLocal,不用撰寫複雜的執行緒共用互斥邏輯。

Thread-Specific Storage模式的意義之一,就是「有時不共用是好的」,如果共用會產生危險,那就不要共用,當然,這種方式所犧牲掉的就是空間,您必須為每一個執行緒保留它們獨立的空間,這是一種以空間換取時間與安全性的方法。

张金鹏 2007-04-17 10:59 发表评论

文章来源:http://www.blogjava.net/jesson2005/articles/111204.html

posted on 2008-09-07 11:06 大石头 阅读(158) 评论(0)  编辑  收藏 所属分类: 多线程


只有注册用户登录后才能发表评论。


网站导航: