首页 > 代码库 > ThreadLocal线程局部变量

ThreadLocal线程局部变量

1.ThreadLocal说明

线程内的共享数据:方法,表达式或者是模块,当他们在同一线程上运行,他们访问同一变量,应该访问的是同一数据。将数据与线程绑定到一起。换句话说,我线程内的事在我的线程内完成,不受其他线程的影响。线程内共享同一数据对象。即在线程内共享,在线程外独立。

public Map<Thread,Integer> threadData=http://www.mamicode.com/new HashMap();threadData.put(Thread.currentthread,data);

ThreadLocal:就相当于一个map,直接向当前线程存取数据。newThreadLocal.set()/get,对于同一个Thread对象而言,一个ThreadLocal对象只能封装一个数据,要封装两个数据则需要两个ThreadLocal独享,若是要封装一百个数据,那么先定义一个对象封装一百个ThreadLocal对象,然后封装这个对象。

ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
 

2.ThreadLocal与synchronized的区别

ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
 
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
 

当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

3.ThreadLocal使用的一般步骤

1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

4.应用code演示

public class ThreadLocalTest {

	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();// 多线程共享数据

	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			// 多个线程往该threadLocal中存入值
			new Thread(new Runnable() {
				@Override
				public void run() {
			//		synchronized (ThreadLocalTest.class) {
						int data = new Random().nextInt();
						System.out.println(Thread.currentThread().getName()
								+ " has put data :" + data);
						threadLocal.set(data);
						MyThreadScopeData.getThreadInstance().setName(
								"name" + data);
						MyThreadScopeData.getThreadInstance().setAge(data);
						// 多个类中读取threadLocal的值,可以看到多个类在同一个线程中共享同一份数据
						new A().get();
						new B().get();
				//	}
				}
			}).start();
		}
	}

	/**
	 * 模拟业务模块A
	 * 
	 * @author Administrator
	 * 
	 */
	static class A {
		public void get() {
			int data = threadLocal.get();
			System.out.println("A from " + Thread.currentThread().getName()
					+ " get data :" + data);
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out
					.println("A from " + Thread.currentThread().getName()
							+ " getMyData: " + myData.getName() + ","
							+ myData.getAge());
		}
	}

	/**
	 * 模拟业务模块B
	 * 
	 * @author Administrator
	 * 
	 */
	static class B {
		public void get() {
			int data = threadLocal.get();
			System.out.println("B from " + Thread.currentThread().getName()
					+ " get data :" + data);
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out
					.println("B from " + Thread.currentThread().getName()
							+ " getMyData: " + myData.getName() + ","
							+ myData.getAge());
		}
	}
}

class MyThreadScopeData {
	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();

	public static MyThreadScopeData getThreadInstance() {
		MyThreadScopeData instance = map.get();
		if (instance == null) {
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}

	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		System.out.println(Thread.currentThread().getName() + " setName :"
				+ name);
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		System.out
				.println(Thread.currentThread().getName() + " setAge :" + age);
		this.age = age;
	}
}
控制台输出结果:

Thread-3 has put data :-1350986975
Thread-1 has put data :-1681440308
Thread-2 has put data :-1348632509
Thread-0 has put data :1315014774
Thread-4 has put data :-129193035
Thread-3 setName :name-1350986975
Thread-3 setAge :-1350986975
Thread-0 setName :name1315014774
Thread-0 setAge :1315014774
Thread-4 setName :name-129193035
Thread-4 setAge :-129193035
Thread-2 setName :name-1348632509
Thread-2 setAge :-1348632509
A from Thread-4 get data :-129193035
Thread-1 setName :name-1681440308
A from Thread-4 getMyData: name-129193035,-129193035
A from Thread-0 get data :1315014774
A from Thread-0 getMyData: name1315014774,1315014774
A from Thread-2 get data :-1348632509
A from Thread-2 getMyData: name-1348632509,-1348632509
A from Thread-3 get data :-1350986975
A from Thread-3 getMyData: name-1350986975,-1350986975
Thread-1 setAge :-1681440308
A from Thread-1 get data :-1681440308
B from Thread-4 get data :-129193035
B from Thread-3 get data :-1350986975
B from Thread-0 get data :1315014774
B from Thread-0 getMyData: name1315014774,1315014774
B from Thread-2 get data :-1348632509
B from Thread-2 getMyData: name-1348632509,-1348632509
B from Thread-4 getMyData: name-129193035,-129193035
A from Thread-1 getMyData: name-1681440308,-1681440308
B from Thread-3 getMyData: name-1350986975,-1350986975
B from Thread-1 get data :-1681440308
B from Thread-1 getMyData: name-1681440308,-1681440308

5.web拓展

 每一个HTTP请求、都是一个独立的线程,有一个独立的ThreadLocal。利用该特性我们可以利用ThreadLocal进行、HTTP请求生命周期的暂时存取值,不同类之间进行传值。比如传递数据库连接等。至此我们在Web应用不用Scope的变量存放值可以利用以下几种手段:

request、ThreadLocal、session、application等。其作用域大家都知道、就不在此详细说明了。现在说明一下request和ThreadLocal的差别。

       1)、存取值方式不同。

             request根据KEY存取值、一个request可以存多个值。

             ThreadLocal只能存一个值,ThreadLocal的get和set方法没有参数KEY。

       2)、使用地方不一样。

             request使用在表示层、一般在Action和Servlet中使用。

             ThreadLocal在什么地方都可以、一般用在框架基类中比较多、比如存放当前的数据库连接等。


转载请声明出处!!!

ThreadLocal线程局部变量