1.代碼示例
package com.landon.mavs.example.concurrent;

import java.util.concurrent.ThreadLocalRandom;


/** *//**
*
* ThreadLocal例子
*
* <pre>
* 1.線程局部變量.其完全不提供鎖.以空間換時(shí)間的手段,為每個(gè)線程提供變量的獨(dú)立副本,以保證線程的安全.
* 2.接口:
* public T get() 返回此線程局部變量的當(dāng)前線程副本中的值
* public void set(T value) 將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為指定值
* public void remove() 移除此線程局部變量當(dāng)前線程的值
* protected T initialValue() 返回此線程局部變量的當(dāng)前線程的“初始值”
* 3.源碼:
* // 1.取得當(dāng)前調(diào)用線程t 2.根據(jù)t獲得ThreadLocalMap【Entry(ThreadLocal k, Object v)】(即每個(gè)線程都有一個(gè)threadLocals)
* // 3. 如果map為不null,則將this和value設(shè)置到map;否則創(chuàng)建map
* public void set(T value) {
* Thread t = Thread.currentThread();
* ThreadLocalMap map = getMap(t);
* if (map != null)
* map.set(this, value);
* else
* createMap(t, value);
* }
*
* // 1.取得當(dāng)前調(diào)用線程t 2.取得t線程關(guān)聯(lián)的ThreadLocalMap
* // 3.如果map不為null則將this作為key得到Entry.然后得到值.
* // 4.如果map為null則返回initialValue(默認(rèn)為null,同時(shí)會(huì)將initialValue set至當(dāng)前線程)
* public T get() {
* Thread t = Thread.currentThread();
* ThreadLocalMap map = getMap(t);
* if (map != null) {
* ThreadLocalMap.Entry e = map.getEntry(this);
* if (e != null)
* return (T)e.value;
* }
* return setInitialValue();
* }.
* 4.注意:
* 1.不同線程間的對(duì)象副本并不是由ThreadLocal創(chuàng)建的.因?yàn)閟et方法未生成任何value的副本.所以將一個(gè)對(duì)象的實(shí)例設(shè)置到不同線程的ThreadLocal中,同樣無(wú)法保證線程安全.
* 所以需要每個(gè)線程內(nèi)創(chuàng)建副本,然后設(shè)置到ThreadLocal中(即ThreadLocal只是一個(gè)key而已.).
* 2.從get方法的最后一句看出,如果沒有set,則第一次調(diào)用get方法時(shí)會(huì)調(diào)用initialValue;可使用匿名內(nèi)部類覆寫此方法,如果不希望初始值為null.另外如果調(diào)用了remove方法,即從
* ThreadLocalMap移除ThreadLocal key,則再次調(diào)用get方法時(shí)依然會(huì)調(diào)用initialValue.
* 3.ThreadLocal實(shí)例通常是類中的 private static final字段(1.其只是用來(lái)做key,和當(dāng)前線程綁定,所以用static方便 2.通常用靜態(tài)方法直接獲取局部變量實(shí)例,
* 如ThreadLocalRandom),它們希望將狀態(tài)與某一個(gè)線程相關(guān)聯(lián).
*
* 5.總結(jié):
* 簡(jiǎn)單來(lái)說(shuō),ThreadLocal就是一個(gè)當(dāng)前線程局部變量表的一個(gè)key,該key對(duì)應(yīng)的V是new出來(lái)的一個(gè)新值,每個(gè)線程的都不一樣.這樣
* 當(dāng)前線程拿到ThreadLocal.get->則直接獲得線程當(dāng)前局部變量表對(duì)應(yīng)的V.(第一次調(diào)用get時(shí)會(huì)將會(huì)將initialValue set,
* 所以初始化ThreadLocal的時(shí)候可覆寫initialValue).
* </pre>
*
* @author landon
*
*/

public class ThreadLocalExample
{
// 一個(gè)線程局部變量
private static final ThreadLocal<ThreadLocalVar> localVar = new ThreadLocal<>();

// 線程局部變量?jī)?nèi)部的V

private static class ThreadLocalVar
{
public int var;
}

// 初始化的時(shí)候指定了localVar2的變量值

private static final ThreadLocal<ThreadLocalVar> localVar2 = new ThreadLocal<ThreadLocalVar>()
{

protected ThreadLocalVar initialValue()
{
return new ThreadLocalVar();
}
};

// 一個(gè)任務(wù)

private static class ThreadLocalTask implements Runnable
{
@Override

public void run()
{
// 注意這里必須要new一個(gè)值,然后set.->即set了當(dāng)前調(diào)用線程中局部變量的值.
ThreadLocalVar var = new ThreadLocalVar();
localVar.set(var);

// 隨機(jī)一個(gè)局部變量的值.這里用到了ThreadLocalRandom,一個(gè)活生生的例子

// public static ThreadLocalRandom current() {
// return localRandom.get();
// }

// 這里初始化了一個(gè)線程局部變量值,是new了一個(gè)ThreadLocalRandom.這個(gè)很關(guān)鍵.
// private static final ThreadLocal<ThreadLocalRandom> localRandom =
// new ThreadLocal<ThreadLocalRandom>() {
// protected ThreadLocalRandom initialValue() {
// return new ThreadLocalRandom();
// }
// };
var.var = ThreadLocalRandom.current().nextInt();

// 打印當(dāng)前調(diào)用線程,即當(dāng)前調(diào)用線程的線程局部變量的值.
System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar:" + localVar.get().var);

// 操作localVar2,將當(dāng)前線程的id復(fù)制給localvar2
localVar2.get().var = (int) Thread.currentThread().getId();

System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar2:" + localVar2.get().var);
}
}

// 從輸出看,ThreadLocalExample#localVar如果不是線程局部變量則多線程操作時(shí),一定要加鎖的.但是如果是線程局部變量,則其只用來(lái)做一個(gè)key而已,局部變量的值是new出來(lái)
// 并設(shè)置,所以每個(gè)線程的ThreadLocal的get值都是互相沒有任何關(guān)系的。
// 多線程之間可隨意操作localVar.
// 同理localvar2

public static void main(String[] args)
{

for (int i = 0; i < 5; i++)
{
new Thread(new ThreadLocalTask(), "Thread-" + i).start();
}

// 主線程操作
localVar.set(new ThreadLocalVar());
localVar.get().var = 100;

System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar:" + localVar.get().var);

// 主線程操作localvar2
localVar2.get().var = 201;

System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar:" + localVar2.get().var);
}
}

2.本篇主要介紹了ThreadLocal的用法,作為解決線程安全的一種方法.ThreadLocalRandom就是一個(gè)典型例子.
posted on 2014-03-05 15:17
landon 閱讀(1826)
評(píng)論(0) 編輯 收藏 所屬分類:
Program