ThreadLocal 直譯為 “線程本地,但如果真那么認(rèn)為就是錯的,ThreadLocal 它是一個容器,用來存放線程的局部變量.ThreadLocal的誕生 是為了解決多線程并發(fā)問題而設(shè)計的,只不過設(shè)計比較難用而已,至今沒有得到廣泛應(yīng)用而已。
一個序列號生成器的程序可能同時會有多個線程并發(fā)訪問它,要保證每個序列號都是自增的,并且互不干擾。
直接上代碼:
先定義了一個接口:
public interface Sequence {
int getNumber();
}
序列號自增
public class ClientThread extends Thread {
private Sequence sequence;
public ClientThread(Sequence sequence) {
this.sequence = sequence;
}
@Override
public void run() {
// super.run();
for (int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"=>"+sequence.getNumber());
}
}
}
此時這里先不用ThreadLocal 來實現(xiàn)
public class SequenceA implements Sequence {
private static int number = 0;
@Override
public int getNumber() {
number = number + 1;
return number;
}
public static void main(String[] args) {
Sequence sequence = new SequenceA();
ClientThread thread1 = new ClientThread(sequence);
ClientThread thread2 = new ClientThread(sequence);
ClientThread thread3 = new ClientThread(sequence);
thread1.start();
thread2.start();
thread3.start();
}
}
看一下運(yùn)行的結(jié)果圖:
這里寫圖片描述
為什么會出現(xiàn)這種效果? 序列號是連續(xù)自增的,共享的static 變量無法保證線程安全,才會出現(xiàn)這種情況的。所以我們要做到線程安全的。
public class SequenceB implements Sequence{
private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() { // 思考 此時為什么是protected?
return 0;
}
};
@Override
public int getNumber() {
numberContainer.set(numberContainer.get()+1);
return numberContainer.get();
}
public static void main(String[] args) {
Sequence sequence = new SequenceB();
ClientThread thread1 = new ClientThread(sequence);
ClientThread thread2 = new ClientThread(sequence);
ClientThread thread3 = new ClientThread(sequence);
thread1.start();
thread2.start();
thread3.start();
}
}
再來看看運(yùn)行的結(jié)果:
這里寫圖片描述
此時每個線程都是安全的,每個線程的數(shù)字都是1-3遞增.ThreadLocal 保證了每個線程都是相互獨(dú)立的,同樣是static 變量,對于不同的線程,它沒有被共享,而是各個線程各一份,這樣就保證了線程的安全。
這里寫圖片描述
這是ThreadLocal 的api
當(dāng)然我們也可以自己來實現(xiàn)一個ThreadLocal
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Created by hello world on 2017/8/8.
*/
public class MyThreadLocal<T> {
private Map<Thread,T> container = Collections.synchronizedMap(new HashMap<Thread, T>()); // 此時 container 是線程安全的map 這是為什么?
// set 方法實現(xiàn)
public void Set(T value){
container.put(Thread.currentThread(),value);
}
//
public T get(){
Thread thread = Thread.currentThread();
T value = container.get(thread);
if(value ==null &&!container.containsKey(thread)){
value = initialValue();
container.put(thread,value);
}
return value;
}
public void remove(){
container.remove(Thread.currentThread());
}
protected T initialValue(){
return null;
}
}
我們再來用MyThreadLocal 來實現(xiàn)一編
public class SequenceC implements Sequence {
private static MyThreadLocal<Integer> numberContainer = new MyThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public int getNumber() {
numberContainer.Set(numberContainer.get()+1);
return numberContainer.get();
}
public static void main(String[] args) {
Sequence sequence = new SequenceB();
ClientThread thread1 = new ClientThread(sequence);
ClientThread thread2 = new ClientThread(sequence);
ClientThread thread3 = new ClientThread(sequence);
thread1.start();
thread2.start();
thread3.start();
}
}
效果都是一樣,就不截圖了。當(dāng)我們遇到static 修飾的變量,多個線程需要獨(dú)享自己static成員變量?
如果需要,可以考慮用ThreadLocal .