Java多線程(四)ThreadLocal入門

JDK提供了ThreadLocal,在一個線程中傳遞同一個對象,相當於保存一個線程的局部變量。

基本使用流程

// 創建ThreadLocal對象
static ThreadLocal<String> threadLocalUser = new ThreadLocal<>();
// 保存對象
threadLocalUser.set("Bob");
// 獲取對象
String current = threadLocalUser.get();
// 移除對象
threadLocalUser.remove();

典型使用方式

void processUser(user) {
  	try {
      	// 保存user對象
      	threadLocalUser.set(user);
      	// 進行傳遞
      	step1();
    } finally {
      	// 保證最後移除,因爲線程可能用完後可能會被放進線程池繼續使用
      	threadLocalUser.remove();
    }
}
void step1() {
  	// 上面保存的user被存在線程中
  	// 通過ThreadLocal來獲取
  	User u = threadLocalUser.get();
  	log(u.name);
  	// 繼續傳遞
  	printUser();
}
void printUser() {
  	// 同樣可以獲取ThreadLocal保存的user對象
  	User u = threadLocalUser.get();
  	println(u.name);
}

案例分析

// 簡單用戶信息類
class User {
  	Stirng name;
  	int level;
  
  	public User(String name, int level) {
      	this.name = name;
      	this.level = level;
    }
}
// 封裝適用於User對象的ThreadLocal
class UserContext implements AutoCloseable {
  	// 全局唯一靜態變量
  	static final ThreadLocal<User> context = new ThreadLocal<>();
  	
  	// 獲取當前線程的ThreadLocal User
  	public static User getCurrentUser() {
      	return context.get();
    }
  
  	// 初始化ThreadLocal的User
  	public UserContext(User user) {
      	context.set(user);
    }
  
  	// 移除ThreadLocal關聯的User
  	public void close() {
      	context.remove();
    }
}

class ProcessThread extends Thread {
  	User user;
  
  	ProcessThread(User user) {
      	this.user = user;
    }
  
  	@Override
  	public void run() {
      	// 創建對象
      	try(UserContext ctx = new UserContext(user)) {
          	// step1 測試傳遞結果
          	new Greeting().hello();
          	// step2 測試傳遞結果
          	Level.checkLevel();
        }
    }
}

class Greeting {
  	void hello() {
      	User user = Usercontext.getCurrentUser();
      	System.out.println("Hello, " + user.name);
    }
}

class Level {
  	static void checkLevel {
      	User user = UserContext.getCurrentUser();
      	if(user.level > 100) {
          	System.out.println(user.name + " is a VIP user.");
        } else {
          	System.out.println(user.name + " is a registered user.");
        }
    }
}

public class Main {
  	public static void main(String[] args) {
      	Thread t1 = new ProcessThread(new User("Bob", 120));
       	Thread t2 = new ProcessThread(new User("Alice", 80));
      	t1.start();
      	t2.start();
      	t1.join();
      	t2.join();
      	System.out.println("Main end");
    }
}

總結

可以把ThreadLocal看成全局Map<Thread, Object>:

  • 每個線程獲取ThreadLocal變量時,使用Thread自身作爲key:
Object threadLocalValue = threadLocalMap.get(Thread.currentThread());
  • ThreadLocal表示線程的“局部變量”, 它確保每個線程的ThreadLocal變量都是各自獨立的。
  • ThreadLocal適合在一個線程的處理流程中保持上下文(避免了同一參數在所有方法中傳遞)
  • 使用ThreadLocal要用try … finally結構
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章