天天看點

ThreadLocal 讀書筆記

1.ThreadLocal是什麼?

從命名角度出發,可以了解為 thread local value(線程局部變量),即為每個線程提供局部變量。與同步機制共享一些變量不同,但是都是可以解決多線程并發的問題,隻是二者面向的問題領域不同而已。

2.ThreadLocal實作機制?

檢視java.lang.ThreadLocal源代碼,我們可以知道其實是使用Map,存儲每個線程的副本。

package java.lang;
import java.lang.ref.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadLocal<T> {
  
     ...

    //初始化變量,子類可重寫此方法
    protected T initialValue() {
        return null;
    }

    //傳回此線程局部變量的目前線程副本中的值。
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

    //将此線程局部變量的目前線程副本中的值設定為指定值。
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
       
         //移除此線程局部變量目前線程的值。
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
    ...
}
           

3.ThreadLocal 的使用

  • 結合單例模式,不同的線程調用get()獲得自己的線程中的對象
import java.util.Vector;

public class SGThreadLocalContext {

	private static ThreadLocal<SGThreadLocalContext> threadLocals = new ThreadLocal<SGThreadLocalContext>();
	
	public static SGThreadLocalContext get() {
		SGThreadLocalContext context = threadLocals.get();
		if (context == null) {
			context = new SGThreadLocalContext();
			threadLocals.set(context);
		}
		return context;
	}

        //屬性
	private String smallGroupAppID;	
	private String userContext;	
	
	public String getSmallGroupAppID() {return smallGroupAppID;}
	public void setSmallGroupAppID(String smallGroupAppID) {
		this.smallGroupAppID = smallGroupAppID;
	}	
	public String getUserContext() {return userContext;}
	public void setUserContext(String userContext) {
		this.userContext = userContext;
	}

}
           
  • Hibernate中,ThreadLocal管理多線程,保證每個線程都有自己的資料庫連接配接
public static final ThreadLocal session = new ThreadLocal();
  public static Session currentSession() {
      Session s = (Session)session.get();
      //open a new session,if this session has none
   if(s == null){
      s = sessionFactory.openSession();
      session.set(s);
   }
      return s;
 } 
           
  • 日志程式,記錄每個線程的活動
package thread;

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LoggerThreadTest {
	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 預設在控制台輸出,添加檔案輸出處理器,輸出XML格式
                logger.addHandler(
                    new FileHandler( Thread.currentThread().getName() + ".log")
                 );
            }catch(IOException e) {
            	e.printStackTrace();
            }

            threadLocal.set(logger);
        }

        return logger;
    }
	/**
	 * 測試日志
	 * @param args
	 */
	public static void main(String[] args) {
		new Test("thread1").start();
        new Test("thread2").start();
        new Test("thread3").start();

	}

}

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

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