ThreadLocal介绍

标签: threadlocal | 发表时间:2013-02-06 17:22 | 作者:
出处:http://www.iteye.com

一、java.lang.ThreadLocal<T>

一个实例就是一个容器,所有可以访问到这个实例的线程都可以在这个容器中存储一个该线程独立使用的变量。

这个实例里面其实是一个Map结构的属性,存储以线程对象为KEY,变量为VALUE的数据。

ThreadLocal实例 线程T1 线程T2 线程T3
tl1 T1:variable_forT1 T2:variable_forT2 T3:variable_forT3
tl2 T1:variable_forT1 T2:variable_forT2 T1:variable_forT3

 

二、ThreadLocal有这样几个方法:

1.T get()

返回当前线程对应的那个变量。

实现是以当前线程为KEY去map属性中去找变量,找到则返回;找不到则初始化一个,set进去,然后返回。

 

2.void set(T value)

给当前线程关联一个变量。

实现是以当前线程为KEY,变量为VALUE存储到map属性中去。

 

3.protected T initialValue()

get方法再找不到当前线程对应的变量时调用这个方法,用于初始化一个变量的值,默认是返回null。可以写一个子类继承ThreadLocal类,重写这个方法以返回需要的值。

 

4.void remove()

删除当前线程对应的变量

实现是删除map属性中当前线程的KEY-VALUE数据。

 

三、ThreadLocal源代码样例

package tp.t1;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ThreadLocal{
	//map属性,用于存储线程对象和变量的对应数据
	private Map values = Collections.synchronizedMap(new HashMap());

	//返回当前线程对应的那个变量
	public Object get(){
			Thread curThread = Thread.currentThread();
			Object o = values.get(curThread);
			if (o == null && !values.containsKey(curThread)){
				o = initialValue();
				values.put(curThread, o);
			}
			return o;
	}

	//给当前线程关联一个变量
	public void set(Object newValue){
		values.put(Thread.currentThread(), newValue);
	}
	
	//初始化一个变量的值
	public Object initialValue(){
		return null;
	}
}

 

四、测试

  • 测试1
package tp.t1;

import tp.t1.SequenceNumber;
import tp.t1.TestThread;
import java.lang.ThreadLocal;

public class Test1 {
	/*
	 * 起两个线程
	 * 每个线程给自己在ThreadLocal实例t1中关联的变量加1后显示出来,循环3次。
	 * 看看每个线程的值会不会互相影响?
	 * 答案是不会影响,每个线程关联的是自己的变量
	 */
	public static void main(String[] args){
		Thread _t1 = new TestThread();
		Thread _t2 = new TestThread();
		_t1.start();
		_t2.start();
	}
}

class SequenceNumber {
	//只需要一个ThreadLocal实例,这里定义在一个类的静态属性中,在各个线程中可以直接访问到。
	private static ThreadLocal<Integer> tl = new ThreadLocal<Integer>(){
		public Integer initialValue(){
			return 0;
		}
	};
	
	//定义一个方法,用于各个线程给自己关联的变量值加1
	public static int getNextNum(){
		SequenceNumber.tl.set(SequenceNumber.tl.get() + 1);
		return SequenceNumber.tl.get();
	}
}

class TestThread extends Thread{
	public void run(){
		for(int i = 0;i < 3;i++){
			int _num = SequenceNumber.getNextNum();
			System.out.println("Thread["+Thread.currentThread().getName()+"]-num["+_num+"]");
		}
	}
}

Thread[Thread-0]-num[1]
Thread[Thread-1]-num[1]
Thread[Thread-0]-num[2]
Thread[Thread-1]-num[2]
Thread[Thread-0]-num[3]
Thread[Thread-1]-num[3]

 

  • 测试2
package tp.t2;

import java.lang.ThreadLocal;

public class Test2 {
	/*
	 * 起两个线程
	 * 每个线程在各自关联的变量是同一个实例,这时每个线程修改实例的内容,输出实例的内容。
	 * 看看每个线程在ThreadLocal对象中,关联的变量是不是copy出来的副本,是不是会互不影响?
	 * 答案是各个线程如果关联同一个变量,那么一个线程修改变量后,另一个线程关联的变量也变化了。
	 */
	public static void main(String[] args){
		//一个ThreadLocal实例,存储的变量时AnObject类型的实例
		ThreadLocal<AnObject> _tl = new ThreadLocal<AnObject>(){
			public AnObject initialValue(){
				return new AnObject();
			}
		};
		
		//一个AnObject类型的实例
		AnObject _anObj = new AnObject();
		_anObj.s = Thread.currentThread().getName();
		
		//关联当前主线程和这个变量
		_tl.set(_anObj);
		
		System.out.println("Thread["+Thread.currentThread().getName()+"]-string["+_tl.get().s+"]");
		
		//起的第一个线程,修改变量的内容,关联变量
		Thread _t1 = new TestThread(_tl,_anObj);
		_t1.start();
		
		//起的第二个线程,修改变量的内容,关联变量
		Thread _t2 = new TestThread(_tl,_anObj);
		_t2.start();
		
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//输出最后这个变量的内容
		System.out.println(_anObj.s);
	}
}

class TestThread extends Thread{
	private ThreadLocal<AnObject> tl;
	private AnObject anObj;
	
	public TestThread(ThreadLocal<AnObject> _tl,AnObject _anObj){
		this.tl = _tl;
		this.anObj = _anObj;
		
		this.anObj.s = this.getName();
		
		//放在这里去关联是不可以的,因为这里的Thread.currentThread()是主线程
		//this.tl.set(this.anObj);
	}
	
	public void run(){
		//当前线程关联变量
		this.tl.set(this.anObj);
		
		System.out.println("Thread["+this.getName()+"]-string["+this.tl.get().s+"]");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Thread["+this.getName()+"]-string["+this.tl.get().s+"]");
	}
}

class AnObject extends Object{
	String s = "";
}

Thread[main]-string[main]
Thread[Thread-0]-string[Thread-1]
Thread[Thread-1]-string[Thread-1]
Thread[Thread-0]-string[Thread-1]
Thread[Thread-1]-string[Thread-1]
Thread-1

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [threadlocal] 推荐:

ThreadLocal介绍

- - ITeye博客
一、java.lang.ThreadLocal. 一个实例就是一个容器,所有可以访问到这个实例的线程都可以在这个容器中存储一个该线程独立使用的变量. 这个实例里面其实是一个Map结构的属性,存储以线程对象为KEY,变量为VALUE的数据. 二、ThreadLocal有这样几个方法:. 返回当前线程对应的那个变量.

正确理解ThreadLocal

- - Java - 编程语言 - ITeye博客
转自: http://www.iteye.com/topic/103804. 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.

ThreadLocal的内存泄露

- - zzm
ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值. 如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序.. 关于的ThreadLocal更多内容,请参考《 ThreadLocal》.

(转)ThreadLocal的内存泄漏问题

- - 编程语言 - ITeye博客
原文:http://www.godiscoder.com/?p=479. 在最近一个项目中,在项目发布之后,发现系统中有内存泄漏问题. 表象是堆内存随着系统的运行时间缓慢增长,一直没有办法通过gc来回收,最终于导致堆内存耗尽,内存溢出. 开始是怀疑ThreadLocal的问题,因为在项目中,大量使用了线程的ThreadLocal保存线程上下文信息,在正常情况下,在线程开始的时候设置线程变量,在线程结束的时候,需要清除线程上下文信息,如果线程变量没有清除,会导致线程中保存的对象无法释放.

ThreadLocal解决dateFormat多线程错误

- - 研发管理 - ITeye博客
出处  http://www.blogjava.net/killme2008/archive/2011/07/10/354062.html.     上周在线上系统发现了两个bug,值得记录下查找的过程和原因. 以后如果还有查找bug比较有价值的经历,我也会继续分享.     第一个bug的起始,是在线上日志发现一个频繁打印的异常——java.lang.ArrayIndexOutOfBoundsException.

java 使用动态代理 - ThreadLocal实现事务管理

- - ITeye博客
动态代理:JDK动态代理只能对实现了接口的类进入代理,采用JDK动态代理必须实现InvocationHandler接口,采用Proxy 类创建相应的代理类.. 下面使用Model2(MVC)使用代理事务查询用户基本信息,使用DB2数据库:. 建立表: create table T_USER (. constraint P_KEY_1 primary key (USER_ID) ); 初始化数据: insert into t_user(user_id, user_name, password) values('root', '系统管理员', 'root');.

POJO中使用ThreadLocal实现Java嵌套事务

- - ImportNew
大多嵌套事务都是通过EJB实现的,现在我们尝试实现对POJO的嵌套事务. 这里我们使用了ThreadLocal的功能. 所以内层事务或外层事务可以在不影响其他事务的条件下进行回滚或提交. 新建的事务嵌套在外层事务中. 如果内层事务完成(不论是回滚或是提交),外层的事务就可以进行回滚或提交,这样的操作并不会影响内层事务.

ThreadLocal原理及其实际应用 - format丶

- - 博客园_首页
java猿在面试中,经常会被问到1个问题:. java实现同步有哪几种方式. 大家一般都会回答使用synchronized, 那么还有其他方式吗. 答案是肯定的, 另外一种方式也就是本文要说的ThreadLocal. ThreadLocal介绍. ThreadLocal, 看名字也能猜到, "线程本地", "线程本地变量".

你确定ThreadLocal真的会造成内存泄露?

- -
1、ThreadLocal知识体系. 本文还是不能免俗,在回答这个问题之前需要先和大家介绍一下ThreadLocal的知识,使大家对ThreadLocal有一个相对全面的认识. ThreadLocal本地线程变量,主要用于解决数据访问的竞争,通常用于多租户、全链路压测、链路跟踪中保存线程上下文环境,在一个请求流转中非常方便的获取一些关键信息,例如当前的租户信息、压测标记.