Java线程本地存储ThreadLocal

ThreadLocal线程范围内的共享数据

线程本地存储是一种自动化机制,可以为相同变量的每个不同线程都创建不同的存储。创建和管理线程本地存储可以由ThreadLocal类实现。

我们先看一个例子

package gemme.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 线程范围内数据共享
 * */
public class ThreadScopeShareData {

	private static Map map = new HashMap<>();
	
	public static void main(String[] args) {
		
		for (int i = 0; i < 2; i++) {
			new Thread() {
				@Override
				public void run() {
					int j = new Random().nextInt();
//					System.out.println(Thread.currentThread().getName() + " create data " + j);
					A a = new A();
					a.put(j);
					a.get();
				}
			}.start();
		}
	}
	
	
	static class A {
		
		public void put(int age) {
			System.out.println(Thread.currentThread().getName() + " put " + age);
			map.put(Thread.currentThread(), new ShareData(age));
		}
		
		public int get() {
			int age = map.get(Thread.currentThread()).getAge();
			System.out.println("A:" + Thread.currentThread().getName() + " get " + age);
			return age;
		}
	} 
}

class ShareData {
	
	private int age;
	
	public ShareData(int age) {
		this.age = age;
	}
	
	public int getAge() {
		return age;
	}
}

运行结果为:

Thread-1 put -529889375
Thread-0 put 622963261
A:Thread-0 get 622963261
A:Thread-1 get -529889375

这种方式很明显有线程安全的隐患,set和get函数没有同步,如果加同步锁又会很影响执行效率。
ThreadLocal其实就是将线程和线程存储的对象做一个映射,各个线程之间互不干扰,在高并发的场景下可以实现无状态的调用。内部结构图如下:

Java线程本地存储ThreadLocal_第1张图片

从上面的结构图,我们已经窥见ThreadLocal的核心机制:

  • 每个Thread线程内部都有一个Map。
  • Map里面存储线程本地对象(key)和线程的变量副本(value)
  • 但是,Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。

所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。

ThreadLocal类提供如下几个核心方法:

  • get():获取当前线程的副本变量值。
  • set():保存当前线程的副本变量值。
  • initialValue():为当前线程初始副本变量值。
  • remove():移除当前前程的副本变量值。

示例代码

package gemme.thread;

import java.util.Random;

public class ThreadLocalTest {

	private static ThreadLocal threadLocal = new ThreadLocal<>();
	
	public static void main(String[] args) {
		
		for (int i = 0; i < 2; i++) {
			new Thread() {
				@Override
				public void run() {
					int data = new Random().nextInt();
					threadLocal.set(new ShareData(data));
					System.out.println(Thread.currentThread().getName() +  " put " + data);
					new A().get();
				}
			}.start();
		}
	}
	
	static class A {
		public int get() {
			int age = threadLocal.get().getAge();
			System.out.println("A:" + Thread.currentThread().getName() + " get " + age);
			return age;
		}
	} 
}

class ShareData {
	private int age;
	
	public ShareData(int age) {
		this.age = age;
	}
	
	public int getAge() {
		return age;
	}
}

运行结果:

Thread-1 put 199906235
Thread-0 put 1281337295
A:Thread-1 get 199906235
A:Thread-0 get 1281337295

总结

  • 每个ThreadLocal只能存一个变量,如果需要存储多个变量,则需要创建多个ThreadLocal
  • 适用于变量独立后不影响业务逻辑的高并发场景。如果如果业务逻辑强依赖于变量,则不适合用ThreadLocal解决。

你可能感兴趣的:(多线程)