1.代码示例
package com.landon.mavs.example.concurrent;
import java.util.concurrent.ThreadLocalRandom;
/** *//**
*
* ThreadLocal例子
*
* <pre>
* 1.线程局部变量.其完全不提供锁.以空间换时间的手段,为每个线程提供变量的独立副本,以保证线程的安全.
* 2.接口:
* public T get() 返回此线程局部变量的当前线程副本中的值
* public void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值
* public void remove() 移除此线程局部变量当前线程的值
* protected T initialValue() 返回此线程局部变量的当前线程的“初始值”
* 3.源码:
* // 1.取得当前调用线程t 2.根据t获得ThreadLocalMap【Entry(ThreadLocal k, Object v)】(即每个线程都有一个threadLocals)
* // 3. 如果map为不null,则将this和value设置到map;否则创建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.取得当前调用线程t 2.取得t线程关联的ThreadLocalMap
* // 3.如果map不为null则将this作为key得到Entry.然后得到值.
* // 4.如果map为null则返回initialValue(默认为null,同时会将initialValue set至当前线程)
* 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.不同线程间的对象副本并不是由ThreadLocal创建的.因为set方法未生成任何value的副本.所以将一个对象的实例设置到不同线程的ThreadLocal中,同样无法保证线程安全.
* 所以需要每个线程内创建副本,然后设置到ThreadLocal中(即ThreadLocal只是一个key而已.).
* 2.从get方法的最后一句看出,如果没有set,则第一次调用get方法时会调用initialValue;可使用匿名内部类覆写此方法,如果不希望初始值为null.另外如果调用了remove方法,即从
* ThreadLocalMap移除ThreadLocal key,则再次调用get方法时依然会调用initialValue.
* 3.ThreadLocal实例通常是类中的 private static final字段(1.其只是用来做key,和当前线程绑定,所以用static方便 2.通常用静态方法直接获取局部变量实例,
* 如ThreadLocalRandom),它们希望将状态与某一个线程相关联.
*
* 5.总结:
* 简单来说,ThreadLocal就是一个当前线程局部变量表的一个key,该key对应的V是new出来的一个新值,每个线程的都不一样.这样
* 当前线程拿到ThreadLocal.get->则直接获得线程当前局部变量表对应的V.(第一次调用get时会将会将initialValue set,
* 所以初始化ThreadLocal的时候可覆写initialValue).
* </pre>
*
* @author landon
*
*/
public class ThreadLocalExample {
// 一个线程局部变量
private static final ThreadLocal<ThreadLocalVar> localVar = new ThreadLocal<>();
// 线程局部变量内部的V
private static class ThreadLocalVar {
public int var;
}
// 初始化的时候指定了localVar2的变量值
private static final ThreadLocal<ThreadLocalVar> localVar2 = new ThreadLocal<ThreadLocalVar>() {
protected ThreadLocalVar initialValue() {
return new ThreadLocalVar();
}
};
// 一个任务
private static class ThreadLocalTask implements Runnable {
@Override
public void run() {
// 注意这里必须要new一个值,然后set.->即set了当前调用线程中局部变量的值.
ThreadLocalVar var = new ThreadLocalVar();
localVar.set(var);
// 随机一个局部变量的值.这里用到了ThreadLocalRandom,一个活生生的例子
// public static ThreadLocalRandom current() {
// return localRandom.get();
// }
// 这里初始化了一个线程局部变量值,是new了一个ThreadLocalRandom.这个很关键.
// private static final ThreadLocal<ThreadLocalRandom> localRandom =
// new ThreadLocal<ThreadLocalRandom>() {
// protected ThreadLocalRandom initialValue() {
// return new ThreadLocalRandom();
// }
// };
var.var = ThreadLocalRandom.current().nextInt();
// 打印当前调用线程,即当前调用线程的线程局部变量的值.
System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar:" + localVar.get().var);
// 操作localVar2,将当前线程的id复制给localvar2
localVar2.get().var = (int) Thread.currentThread().getId();
System.out.println("curThread:" + Thread.currentThread().getName()
+ " localVar2:" + localVar2.get().var);
}
}
// 从输出看,ThreadLocalExample#localVar如果不是线程局部变量则多线程操作时,一定要加锁的.但是如果是线程局部变量,则其只用来做一个key而已,局部变量的值是new出来
// 并设置,所以每个线程的ThreadLocal的get值都是互相没有任何关系的。
// 多线程之间可随意操作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就是一个典型例子.
posted on 2014-03-05 15:17
landon 阅读(1820)
评论(0) 编辑 收藏 所属分类:
Program