Java 并发编程之ThreadLocal详解及实例

一、概述

ThreadLocal的名称比较容易让人误解,会认为其是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量。

其设计的初衷是为了解决多线程编程中的资源共享问题。提起这个,大家一般会想到synchronized,synchronized采取的是“以时间换空间”的策略,本质上是对关键资源上锁,让大家排队操作。而ThreadLocal采取的是“以空间换时间”的思路,为每个使用该变量的线程提供独立的变量副本,在本线程内部,它相当于一个“全局变量”,可以保证本线程任何时间操纵的都是同一个对象。

二、实例

下面用一个实例阐述ThreadLocal的使用方法

1.  创建一个Context类,其中含有transactionId属性。 

2. 创建MyThreadLocal做为容器,将一个Context对象保存于ThreadLocal中

3.模拟业务层,在某处读取context对象

4.多线程客户端程序,用于测试

package ThreadLocalTest;

/**

* Created by majian on 2018/7/5.

* 创建一个Context类,其中含有transactionId属性。

*/

public class Context {

private StringtransactionId =null;

public String getTransactionId() {

return transactionId;

}

public void setTransactionId(String transactionId) {

this.transactionId = transactionId;

}

}



package ThreadLocalTest;

/**

* Created by majian on 2018/7/5.

* 创建MyThreadLocal做为容器,将一个Context对象保存于ThreadLocal中

*/

public class MyThreadLocal {

public static final ThreadLocaluserThreadLocal =new ThreadLocal();

public static void set(Context user) {

userThreadLocal.set(user);

}

public static Context get() {

return (Context)userThreadLocal.get();

}

/* public static void unset() {

userThreadLocal.remove();

}*/

}


package ThreadLocalTest;

/**

* Created by majian on 2018/7/5.

*/

public class BusinessService {

public void businessMethod() {

Context context = MyThreadLocal.get();

System.out.println(context.getTransactionId());

}

}


package ThreadLocalTest;

import java.util.Random;

/**

* Created by majian on 2018/7/5.

* 多线程客户端程序,用于测试

*/

public class ThreadLocalDemoextends Thread {

public static void main(String[] args) {

Thread threadone =new ThreadLocalDemo();

threadone.start();

Thread threadtwo =new ThreadLocalDemo();

threadtwo.start();

}

@Override

    public void run() {

Context context =new Context();

Random random =new Random();

int age= random.nextInt(100);

context.setTransactionId(String.valueOf(age));

System.out.println("set thread [" + getName() +"] contextid to " + String.valueOf(age));

// 在ThreadLocal中设置context

        MyThreadLocal.set(context);

/* note that we are not explicitly passing the transaction id */

        try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

new BusinessService().businessMethod();

//        MyThreadLocal.unset();

    }

}


 运行结果:



二. 深入分析ThreadLocal类


 下面,我们来看一下 ThreadLocal 的具体实现,该类一共提供的四个方法:

publicT get() { }

public void set(T value) { }

public void remove() { }

protected T initialValue() { }

 其中,get()方法是用来获取 ThreadLocal变量 在当前线程中保存的值,set() 用来设置 ThreadLocal变量 在当前线程中的值,remove() 用来移除当前线程中相关 ThreadLocal变量,initialValue() 是一个 protected 方法,一般需要重写。

至此,可能大部分朋友已经明白了 ThreadLocal类 是如何为每个线程创建变量的副本的:

① 在每个线程Thread内部有一个 ThreadLocal.ThreadLocalMap 类型的成员变量 threadLocals,这个threadLocals就是用来存储实际的ThreadLocal变量副本的,键值为当前ThreadLocal变量,value为变量的副本(值);

  ② 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的值为value,存到 threadLocals;

  ③ 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在对应线程的threadLocals里面查找。

你可能感兴趣的:(Java 并发编程之ThreadLocal详解及实例)