Java多線程之ThreadLocal和InheritableThreadLocal的使用


類變量的共享可以使用public static變量的形式,所有的線程都使用同一個public static變量,如果想實現每一個線程都有自己的共享變量該如何解決呢? JDK中提供的ThreadLocal正式爲了解決這樣的問題。


類ThreadLocal主要解決的就是每個線程綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有數據(比如用戶信息)

ThreadLocal的使用


ThreadLocal中常用的方法:


get():返回此線程局部變量的當前線程副本中的值。
initialValue():返回此線程局部變量的當前線程的“初始值”。
remove():移除此線程局部變量當前線程的值。
set(T value):將此線程局部變量的當前線程副本中的值設置爲指定值。



方法get()與null

public class Run {
    public static ThreadLocal tl = new ThreadLocal();

	public static void main(String[] args) {
		if (tl.get() == null) {
			System.out.println("從未放過值");
			tl.set("我的值");
		}
		System.out.println(tl.get());
		System.out.println(tl.get());
	}

}

運行結果

從未放過值
我的值
我的值

從圖中運行結果來看,第一次調用t1對象的get()方法返回的值是null,通過調用set()方法賦值後順利取出結果打印在控制檯, 類ThreadLocal解決的是變量在不同線程之間的隔離性,也就是不同線程擁有自己的值,不同線程中的值是可以放入ThreadLocal中進行保存的.


驗證線程的隔離性

public class ThreadA extends Thread {

	@Override
	public void run() {
		try {
			for (int i = 0; i < 100; i++) {
				if (Tools.tl.get() == null) {
					Tools.tl.set("ThreadA" + (i + 1));
				} else {
					System.out.println("ThreadA get Value=" + Tools.tl.get());
				}
				Thread.sleep(200);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}


public class ThreadB extends Thread {

	@Override
	public void run() {
		try {
			for (int i = 0; i < 100; i++) {
				if (Tools.tl.get() == null) {
					Tools.tl.set("ThreadB" + (i + 1));
				} else {
					System.out.println("ThreadB get Value=" + Tools.tl.get());
				}
				Thread.sleep(200);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}


public class Tools {

	public static ThreadLocal tl = new ThreadLocal();

}

public class Run {
	public static void main(String[] args) {

		try {
			ThreadA a = new ThreadA();
			ThreadB b = new ThreadB();
			a.start();
			b.start();

			for (int i = 0; i < 100; i++) {
				if (Tools.tl.get() == null) {
					Tools.tl.set("Main" + (i + 1));
				} else {
					System.out.println("Main get Value=" + Tools.tl.get());
				}
				Thread.sleep(200);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}


執行結果:



雖然3個線程都在往t1中set()數據,但是每個線程都能取出自己的數據.


解決get()返回null的問題

public class ThreadLocalExt extends ThreadLocal {
	@Override
	protected Object initialValue() {
		return "我是默認值 第一次get不再爲null";
	}
}

public class Run {
	public static ThreadLocalExt tl = new ThreadLocalExt();

	public static void main(String[] args) {
		if (tl.get() == null) {
			System.out.println("從未放過值");
			tl.set("我的值");
		}
		System.out.println(tl.get());
		System.out.println(tl.get());
	}

}

運行結果:

我是默認值 第一次get不再爲null
我是默認值 第一次get不再爲null


InheritableThreadLocal的使用

InheritableThreadLocal: 可繼承的ThreadLocal

使用類InheritableThreadLocal可以在子線程中獲得父線程繼承下來的值

值繼承


public class InheritableThreadLocalExt extends InheritableThreadLocal {
	@Override
	protected Object initialValue() {
		return System.currentTimeMillis();
	}
}

public class Tools {

	public static InheritableThreadLocalExt tl = new InheritableThreadLocalExt();

}


public class ThreadA extends Thread {

	@Override
	public void run() {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在ThreadA線程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

public class ThreadB extends Thread {

	@Override
	public void run() {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在ThreadB線程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

public class Run {

	public static void main(String[] args) {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在Main線程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
			Thread.sleep(5000);
			ThreadA a = new ThreadA();
			ThreadB b = new ThreadB();
			a.start();
			b.start();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

測試結果:



值繼承再修改

如果在繼承的同事還可以對值進一步的處理就更好了. 可以覆蓋父類的clidValue方法

public class InheritableThreadLocalExt extends InheritableThreadLocal {
	@Override
	protected Object initialValue() {
		return System.currentTimeMillis();
	}

	@Override
	protected Object childValue(Object parentValue) {
		return parentValue + " 我在子線程加的~!";
	}
}



但在使用 InheritableThreadLocalExt類需要注意一點的是,如果子線程在取得值的同時,主線程將InheritableThreadLocalExt中的值進行更改,那麼子線程取到的值還是舊值.

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