ThreadLocal

一. 用法 ThreadLocal用于保存某个线程共享变量; 对于同一个static ThreadLocal,不一样线程只能从中get,set,remove本身的变量,而不会影响其余线程的变量 1. ThreadLocal.get : 获取ThreadLocal中当前线程共享变量的值; 2. ThreadLocal.set : 设置ThreadLocal中当前线程共享变量的值; 3. ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值; 4. ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。缓存

public class MyThreadLocal {
	
	public static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(){
		protected Object initialValue(){
			System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
			return null;
		}
	};
}
public class MyIntegerTask implements Runnable {

	private String name;
	
    public MyIntegerTask() {
	}

    public MyIntegerTask(String name){
    	this.name = name;
    }
    
	@Override
	public void run() {
		for (int i = 0; i < 5; i++){
			if (null == MyThreadLocal.threadLocal.get()){
				MyThreadLocal.threadLocal.set(0);
				System.out.println("线程" + name + ":0");
			} else {
				int num = (int) MyThreadLocal.threadLocal.get();
				MyThreadLocal.threadLocal.set(num + 1);
				System.out.println("线程" + name + ":" + MyThreadLocal.threadLocal.get());
				
				if (i == 3){
					MyThreadLocal.threadLocal.remove();
				}
			}
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class MyStringTask implements Runnable {

	private String name;
	
    public MyStringTask() {
	}

    public MyStringTask(String name){
    	this.name = name;
    }
    
	@Override
	public void run() {
		
		for (int i = 0; i < 5; i++){
			if (null == MyThreadLocal.threadLocal.get()){
				MyThreadLocal.threadLocal.set("a");
				System.out.println("线程" + name + ":a");
			} else {
				String str = (String) MyThreadLocal.threadLocal.get();
				MyThreadLocal.threadLocal.set(str + "a");
				System.out.println("线程" + name  + ":" + MyThreadLocal.threadLocal.get());
			}
			
			try {
				Thread.sleep(800);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class Test {
	
	public static void main(String[] args) {
		new Thread(new MyIntegerTask("IntegerTask1")).start();
		new Thread(new MyStringTask("StringTask1")).start();
		new Thread(new MyIntegerTask("IntegerTask2")).start();
		new Thread(new MyStringTask("StringTask2")).start();
	}
}

run 执行结果jvm

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2:0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask2:a
线程StringTask1:a
线程IntegerTask1:0
线程StringTask1:aa
线程StringTask2:aa
线程IntegerTask2:1
线程IntegerTask1:1
线程StringTask1:aaa
线程StringTask2:aaa
线程IntegerTask1:2
线程IntegerTask2:2
线程StringTask1:aaaa
线程StringTask2:aaaa
线程IntegerTask1:3
线程IntegerTask2:3
线程StringTask2:aaaaa
线程StringTask1:aaaaa
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2:0
线程IntegerTask1:0

debug执行结果ide

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1:0
线程IntegerTask2:0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask2:a
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask1:a
线程StringTask2:aa
线程StringTask1:aa
线程IntegerTask1:1
线程IntegerTask2:1
线程StringTask2:aaa
线程StringTask1:aaa
线程IntegerTask1:2
线程IntegerTask2:2
线程StringTask2:aaaa
线程StringTask1:aaaa
线程IntegerTask1:3
线程IntegerTask2:3
线程StringTask2:aaaaa
线程StringTask1:aaaaa
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2:0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1:0
**_

### ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
JDWP exit error AGENT_ERROR_NO_JNI_ENV(183):  [util.c:840]
_**

2、原理 线程共享变量缓存以下: Thread.ThreadLocalMap<ThreadLocal,Object>;性能

  1. Thread: 当前线程,能够经过Thread.currentThread()获取;
  2. ThreadLocal: 咱们的static ThreadLocal 变量
  3. Object: 当前线程共享变量

咱们调用ThreadLocal.get方法时,其实是从当前线程中获取ThreadLocalMap<ThreadLocal, Object>,而后根据当前ThreadLocal获取当前线程共享变量Object。this

ThreadLocal.set,ThreadLocal.remove其实是一样的道理。线程

这种存储的好处:debug

  1. 当线程死去的时候,线程共享变量ThreadLocalMap则销毁
  2. ThreadLocalMap<ThreadLocal,Object> 键值对数量为ThreadLocal 的数量,通常来讲ThreadLocal数量不多,相比在ThreadLocal中使用Map<Thread,Object>键值对存储共享变量,性能提升了不少;

关于ThreadLocalMap<ThreadLocal, Object>弱引用问题:code

当线程没有结束,可是ThreadLocal已经被回收,则可能致使线程中存在ThreadLocalMap<null, Object>的键值对,形成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。图片

虽然ThreadLocal的get,set方法能够清除ThreadLocalMap中key为null的value,可是get,set方法在内存泄露后并不会必然调用,因此为了防止此类状况的出现,咱们有两种手段。内存

一、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

二、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

输入图片说明

相关文章
相关标签/搜索