ThreadLocal實現線程範圍的共享變量

線程範圍內共享數據的示意圖



ThreadLocal的作用和目的: 用於實現線程內的數據共享, 即對於相同的程序代碼, 多個模塊在同一線程中運行時要共享一份數據, 而在另外線程中運行時又共享另外一份數據
每個線程調用全局ThreadLocal對象的set方法, 就相當於往其內部的map中增加一條記錄, key分別是各自的線程, value是各自的set方法傳遞進去的值. 在線程結束時可以調用ThreadLocal.clear()方法, 這樣會更快釋放內存, 不調用也可以, 因爲線程結束後也可以自動釋放相關的ThreadLocal變量

ThreadLocal的應用場景

訂單處理包含一系列操作: 減少庫存量, 增加一條流水臺賬, 修改總賬, 這幾個操作要在同一個事務中完成, 通常也是在同一個線程中進行處理, 如果公司應收款的操作失敗了, 則應該把前面的操作回滾, 否則, 提交所有操作, 這要求這些操作使用相同的數據庫連接對象, 而這些操作的代碼分別位於不同的模塊類中

Struts2的ActionContext, 同一段代碼被不同的線程調用運行時, 該代碼操作的數據是每個線程各自的狀態和數據, 對於不同的線程來說, getContext()方法拿到的對象都不相同, 讀同一個線程來說, 不管調用getContext()方法多少次和在哪個模塊中getContext()方法, 拿到的都是同一個

舉例說明 創建三個線程, 他們都訪問了三個對象, 第一個對象設置值, 第二三個對象取值, 同一個線程設置的值, 只能被相同的線程獲取


實現對ThreadLocal變量的封裝, 讓外界不要直接操作ThreadLocal變量
對基本類型的數據的封裝, 這種應用相對很少見
對對象類型的數據的封裝, 比較常見, 即讓某個類針對不同線程分別創建一個獨立的實例對象

總結: 一個ThreadLocal代表一個變量, 故其中只能放一種數據, 你有兩個變量都要線程範圍內共享, 則要定義兩個ThreadLocal對象, 如果有一百個變量要線程共享呢? 那請定義一個對象來封裝, 然後在ThreadLocal中存儲這一個對象

代碼

package thread;

import java.util.Random;

public class ThreadLocalTest {

	public static void main(String[] args) {
		for (int i = 0; i < 2; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
					String name = new Random().nextInt() + "";
					int age = new Random().nextInt();
					threadInstance.setName(name);
					threadInstance.setAge(age);
					System.out.println(Thread.currentThread().getName() + " has put name : " + name);
					System.out.println(Thread.currentThread().getName() + " has put age : " + age);
					new A().get();
					new B().get();
				}
			}, "thread " + i).start();
		}
	}

	// A B 兩個模塊
	static class A {
		public void get() {
			MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
			System.out.println("A from " + Thread.currentThread().getName() + " get name : " + threadInstance.getName());
			System.out.println("A from " + Thread.currentThread().getName() + " get age : " + threadInstance.getAge());
		}
	}

	static class B {
		public void get() {
			MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
			System.out.println("B from " + Thread.currentThread().getName() + " get name : " + threadInstance.getName());
			System.out.println("B from " + Thread.currentThread().getName() + " get age : " + threadInstance.getAge());
		}
	}
}

class MyThreadScopeData {// 線程範圍內共享類 把線程內需要共享的變量 通通封裝到這個類中 然後在用ThreadLocal 封裝起來

	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<>();

	// 不允許直接創建該對象
	private MyThreadScopeData() {
	}

	// 提供靜態方法 返回和線程相關的實例數據對象
	public static MyThreadScopeData getThreadInstance() {
		// 這樣外部就看不到ThreadLocal對象了
		MyThreadScopeData instance = map.get();
		if (instance == null) {
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}

	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

發佈了31 篇原創文章 · 獲贊 10 · 訪問量 9399
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章