ThreadLocal是线程内的全局上下文。就是在单个线程中,方法之间共享的内存,每个方法都可以从该上下文中获取值和修改值。
在业务开发中,ThreadLocal 有两种典型的使用场景
- ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
- ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。
场景1
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
String data = new Demo().date(finalI);
System.out.println(data);
}).start();
Thread.sleep(100);
}
}
private String date(int seconds){
Date date = new Date(1000 * seconds);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
return simpleDateFormat.format(date);
}
打印
00:00
00:01
00:02
00:03
00:04
00:05
00:06
00:07
00:08
00:09
给每个线程都创建了SimpleDateFormat对象,他们之间互不影响,代码是可以正常执行的。
如果有1000个线程都用到SimpleDateFormat对象呢,对这种场景,ThreadLocal再合适不过了,ThreadLocal给每个线程维护一个自己的simpleDateFormat对象,这个对象在线程之间是独立的,互相没有关系的。这也就避免了线程安全问题。与此同时,simpleDateFormat对象还不会创造过多,线程池一共只有 16 个线程,所以需要16个对象即可。
public static ExecutorService threadPool = Executors.newFixedThreadPool(16);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(() -> {
String data = new Demo().date(finalI);
System.out.println(data);
});
}
threadPool.shutdown();
}
private String date(int seconds){
Date date = new Date(1000 * seconds);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
ThreadLocalUtil.set("dateFormat",simpleDateFormat);
SimpleDateFormat dateFormat = ThreadLocalUtil.get("dateFormat");
return dateFormat.format(date);
}
场景2
传统方式我们要在方法中访问某个变量,可以通过传参的形式往方法中传参,如果多个方法都要使用那么每个方法都要传参;如果使用ThreadLocal所有方法就不需要传该参数了,每个方法都可以通过ThreadLocal来访问该值。
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
//User user = new User("曹操");
//new Service1().service1(user);
for (int i = 0; i < 10; i++) {
int m = i;
new Thread(() -> {
User user = new User("曹操" + m);
new Service1().service1(user);
}).start();
Thread.sleep(100);
}
}
}
class Service1 {
public void service1(User user){
//给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了
ThreadLocalUtil.set("user",user);
new Service2().service2();
}
}
class Service2 {
public void service2(){
User user = ThreadLocalUtil.get("user");
System.out.println("service2拿到的用户:"+user.name);
new Service3().service3();
}
}
class Service3 {
public void service3(){
User user = ThreadLocalUtil.get("user");
System.out.println("service3拿到的用户:"+user.name);
//在整个流程执行完毕后,一定要执行remove
ThreadLocalUtil.remove();
}
}
class User {
String name;
public User(String name){
this.name = name;
}
}
public class ThreadLocalUtil<T> {
private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal() {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>(4);
}
};
public static Map<String, Object> getThreadLocal(){
return threadLocal.get();
}
public static <T> T get(String key) {
Map map = (Map)threadLocal.get();
return (T)map.get(key);
}
public static <T> T get(String key,T defaultValue) {
Map map = (Map)threadLocal.get();
return (T)map.get(key) == null ? defaultValue : (T)map.get(key);
}
public static void set(String key, Object value) {
Map map = (Map)threadLocal.get();
map.put(key, value);
}
public static void set(Map<String, Object> keyValueMap) {
Map map = (Map)threadLocal.get();
map.putAll(keyValueMap);
}
public static void remove() {
threadLocal.remove();
}
public static <T> T remove(String key) {
Map map = (Map)threadLocal.get();
return (T)map.remove(key);
}
public static <T> Map<String,T> fetchVarsByPrefix(String prefix) {
Map<String,T> vars = new HashMap<>();
if( prefix == null ){
return vars;
}
Map map = (Map)threadLocal.get();
Set<Map.Entry> set = map.entrySet();
for( Map.Entry entry : set){
Object key = entry.getKey();
if( key instanceof String ){
if( ((String) key).startsWith(prefix) ){
vars.put((String)key,(T)entry.getValue());
}
}
}
return vars;
}
public static void clear(String prefix) {
if( prefix == null ){
return;
}
Map map = (Map)threadLocal.get();
Set<Map.Entry> set = map.entrySet();
List<String> removeKeys = new ArrayList<>();
for( Map.Entry entry : set ){
Object key = entry.getKey();
if( key instanceof String ){
if( ((String) key).startsWith(prefix) ){
removeKeys.add((String)key);
}
}
}
for( String key : removeKeys ){
map.remove(key);
}
}
}
打印
service2拿到的用户:曹操0
service3拿到的用户:曹操0
service2拿到的用户:曹操1
service3拿到的用户:曹操1
service2拿到的用户:曹操2
service3拿到的用户:曹操2
service2拿到的用户:曹操3
service3拿到的用户:曹操3
service2拿到的用户:曹操4
service3拿到的用户:曹操4
service2拿到的用户:曹操5
service3拿到的用户:曹操5
service2拿到的用户:曹操6
service3拿到的用户:曹操6
service2拿到的用户:曹操7
service3拿到的用户:曹操7
service2拿到的用户:曹操8
service3拿到的用户:曹操8
service2拿到的用户:曹操9
service3拿到的用户:曹操9