06-ThreadLocal类及应用技巧
ThreadLocal是什么
早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
- void set(Object value)设置当前线程的线程局部变量的值。
- public Object get()该方法返回当前线程所对应的线程局部变量。
- public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
范例:
package cn.itcast.demo.thread;
import java.util.Random;
public class ThreadLocalTest {
//private static Map<Thread, Integer> threadMap = new HashMap<Thread, Integer>();
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); // 当前线程的局部变量
public static void main(String[] args) {
// 两个线程
for (int i=0; i<2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
//threadMap.put(Thread.currentThread(), data);
threadLocal.set(data); // 替换上面的代码
System.out.println(Thread.currentThread().getName() + " has put data: " + data);
new A().get();
new B().get();
}
}).start();
}
}
// A模块
static class A {
public void get() {
//int data = threadMap.get(Thread.currentThread());
int data = threadLocal.get(); // 替换上面的代码
System.out.println("A from " + Thread.currentThread().getName() + " get data: " + data);
}
}
// B模块
static class B {
public void get() {
//int data = threadMap.get(Thread.currentThread());
int data = threadLocal.get(); // 替换以上的代码
System.out.println("B from " + Thread.currentThread().getName()+ " get data: " + data);
}
}
}
输出:
Thread-0 has put data: -231837341 Thread-1 has put data: -471011850 A from Thread-1 get data: -471011850 A from Thread-0 get data: -231837341 B from Thread-0 get data: -231837341 B from Thread-1 get data: -471011850
一个ThreadLocal对象只能存放一个变量,如果要放多个变量,那么就创建多个ThreadLocal对象,或者把所有属性都封装成一个实体类(或者集合),把整个实体类对象放入到ThreadLocal对象中,就可以实现多个变量存放的操作
package cn.itcast.demo.thread;
import java.util.Random;
public class ThreadLocalTest {
private static ThreadLocal<User> threadLocal = new ThreadLocal<User>(); // 当前线程的局部变量
public static void main(String[] args) {
// 两个线程
for (int i=0; i<2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 为了区别不同的数据,这里需要一个随机数
int args = new Random().nextInt(100);
// 准备放入线程的数据
User user = new User();
user.setUsername("Peter: " + args);
user.setAge(20 + args);
// 放入当前线程的局部变量中
threadLocal.set(user);
// 控制台打印结果
System.out.println(Thread.currentThread().getName() + " has put data: " + user);
// 线程调用模块
new A().get();
new B().get();
}
}).start();
}
}
// A模块
static class A {
public void get() {
User user = threadLocal.get();
System.out.println("A from " + Thread.currentThread().getName() + " get data: " + user);
}
}
// B模块
static class B {
public void get() {
User user = threadLocal.get();
System.out.println("B from " + Thread.currentThread().getName() + " get data: " + user);
}
}
}
// User实体类
class User {
// 两个属性
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "【 username: {" + this.username + "}, {age: " + this.age + "} 】";
}
}
打印结果:
Thread-1 has put data: 【 username: {Peter: 18}, {age: 38} 】
Thread-0 has put data: 【 username: {Peter: 16}, {age: 36} 】
A from Thread-1 get data: 【 username: {Peter: 18}, {age: 38} 】
A from Thread-0 get data: 【 username: {Peter: 16}, {age: 36} 】
B from Thread-0 get data: 【 username: {Peter: 16}, {age: 36} 】
B from Thread-1 get data: 【 username: {Peter: 18}, {age: 38} 】
改善以上的代码,因为一个线程就需要一个ThreadLocal变量,如果有很多个线程,那么就要很多个变量,这样性能大大降低,所以要使用单例设计模式
以后任何一个模块或者线程调用这个实体类,就会自带线程范围变量,因为这个类在设计的时候本身就具有线程范围的变量的特点
应用场景:

package cn.itcast.demo.thread;
import java.util.Random;
public class ThreadLocalTest {
private static ThreadLocal<User> threadLocal = new ThreadLocal<User>(); // 当前线程的局部变量
public static void main(String[] args) {
// 两个线程
for (int i=0; i<2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 为了区别不同的数据,这里需要一个随机数
int args = new Random().nextInt(100);
// 准备放入线程的数据
User user = User.getThreadInstance();
user.setUsername("Peter: " + args);
user.setAge(args);
// 放入当前线程的局部变量中
threadLocal.set(user);
// 控制台打印结果
System.out.println(Thread.currentThread().getName() + " has put data: " + user);
// 线程调用模块
new A().get();
new B().get();
}
}).start();
}
}
// A模块
static class A {
public void get() {
User user = User.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName() + " get data: " + user);
}
}
// B模块
static class B {
public void get() {
User user = User.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName() + " get data: " + user);
}
}
}
// User实体类, 【单例设计模式】
class User {
// 1.构造私有化
private User() {}
// 2.提高外界创建本实例对象的静态方法【可以使用懒汉式,也可以使用饿汉式(使用饿汉式时要考虑到线程的安全问题)】
private static User user = null;
private static ThreadLocal<User> userThread = new ThreadLocal<User>();
public static synchronized User getThreadInstance() {
user = userThread.get();
if (user == null) {
// 这里在赋值之前可能会出现线程不安全,即在A线程赋值之前,B线程也进来了,所以要+synchronized进行线程互斥,饿汉式不需要
user = new User();
userThread.set(user);
}
return user;
}
/* 饿汉式
User user = new User();
public static User getThreadInstance() {
return user;
}
*/
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "【 username: {" + this.username + "}, {age: " + this.age + "} 】";
}
}
打印结果:
Thread-0 has put data: 【 username: {Peter: 52}, {age: 52} 】
Thread-1 has put data: 【 username: {Peter: 79}, {age: 79} 】
A from Thread-0 get data: 【 username: {Peter: 52}, {age: 52} 】
A from Thread-1 get data: 【 username: {Peter: 79}, {age: 79} 】
B from Thread-0 get data: 【 username: {Peter: 52}, {age: 52} 】
B from Thread-1 get data: 【 username: {Peter: 79}, {age: 79} 】

浙公网安备 33010602011771号