類變量的共享可以使用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的使用
值繼承
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中的值進行更改,那麼子線程取到的值還是舊值.