多线程之线程范围内的数据共享ThreadLocal

如果多个线程使用同一个数据,那么如何保证线程范围内的数据共享。

我们可以使用一个map来存储当前线程,以及其数据如下:

package andy.thread.traditional.test;

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

/**
 * @author Zhang,Tianyou
 * @version 2014年11月8日 下午2:12:44
 */
// 线程范围内的共享数据
public class ThreadScopeSharedData {

	private static int data = 0;
	//ThreadLocal  提供了线程范围内的数据共享  只能存贮一个数据
	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	//private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();

	public static void main(String[] args) {
		for (int i = 0; i < 2; i++) {

			new Thread(new Runnable() {

				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()
							+ " has put data :" + data);
					threadData.put(Thread.currentThread(), data);
					//x.set(data);
					new A().get();
					new B().get();

				}
			}).start();
		}

	}

	static class A {
		public void get() {
			int data = threadData.get(Thread.currentThread());
			//int data = x.get();
			System.out.println("A from " + Thread.currentThread().getName()
					+ " get data :" + data);
		}
	}

	static class B {
		public void get() {
			int data = threadData.get(Thread.currentThread());
			//int data = x.get();
			System.out.println("B from " + Thread.currentThread().getName()
					+ " get data :" + data);
		}
	}

}

运行效果如下:

Thread-1 has put data :-591164568
Thread-0 has put data :2032497041
A from Thread-0 get data :2032497041
A from Thread-1 get data :-591164568
B from Thread-0 get data :2032497041
B from Thread-1 get data :-591164568

但JDK也为我们提供了另一种实现方式:ThreadLocal类,它能够实现各自线程局部变量,并且独立于变量的初始化副本。

public class ThreadLocal<T>
extends Object

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 getset 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

例如,以下类生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

  import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum =
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };

     public static int getCurrentThreadId() {
         return uniqueId.get();
     }
 } // UniqueThreadIdGenerator
 

每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

上面代码的更优雅的实现如下:

package andy.thread.traditional.test;

import java.util.Random;

/**
 * @author Zhang,Tianyou
 * @version 2014年11月8日 下午2:26:24
 */

public class ThreadLocalTest {

	private static int data = 0;
	// ThreadLocal 提供了线程范围内的数据共享

	public static void main(String[] args) {
		for (int i = 0; i < 2; i++) {

			new Thread(new Runnable() {

				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()
							+ " has put data :" + data);
					MyThreadScopeData.getThreadScopeInstance().setName(
							"name = " + data);
					MyThreadScopeData.getThreadScopeInstance().setAge(data);

					new A().get();
					new B().get();

				}
			}).start();
		}

	}

	static class A {
		public void get() {
			MyThreadScopeData myThreadScopeData = MyThreadScopeData
					.getThreadScopeInstance();

			System.out.println("A from " + Thread.currentThread().getName()
					+ " Name :" + myThreadScopeData.getName() + "age = "
					+ myThreadScopeData.getAge());
		}
	}

	static class B {
		public void get() {
			MyThreadScopeData myThreadScopeData = MyThreadScopeData
					.getThreadScopeInstance();

			System.out.println("B from " + Thread.currentThread().getName()
					+ " Name :" + myThreadScopeData.getName() + "age = "
					+ myThreadScopeData.getAge());
		}
	}
}

class MyThreadScopeData {
	private MyThreadScopeData() {
	}

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

	// 此处不需要synchronized 应该每个线程操作各自的数据
	public static MyThreadScopeData getThreadScopeInstance() {
		// 返回此线程局部变量的当前线程副本中的值。
		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) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

其运行效果如下:

Thread-1 has put data :1322483490
A from Thread-1 Name :name = 1322483490age = 1322483490
B from Thread-1 Name :name = 1322483490age = 1322483490
Thread-0 has put data :1149801771
A from Thread-0 Name :name = 1149801771age = 1149801771
B from Thread-0 Name :name = 1149801771age = 1149801771
时间: 2024-10-25 00:12:41

多线程之线程范围内的数据共享ThreadLocal的相关文章

4、线程范围内的数据共享之ThreadLocal

1 /** 2 * 线程范围类的数据共享 3 * 核心:ThreadLocal类 4 * 实际场景: 5 * Hibernate的getCurrentSession方法,就是从线程范围内获取存在的session,如果不存在则新建一个并绑定到线程上 6 * struts将一个请求里的所有参数绑定到一个线程范围内的对象里 7 * @author yzl 8 * 9 */ 10 public class ThreadPart_4 { 11 public static void main(String[

Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类

1.线程范围内共享变量 1.1 前奏: 使用一个Map来实现线程范围内共享变量 public class ThreadScopeShareData { static Map<Thread, Integer> dataMap = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable

单线程范围内的数据共享_ThreadLocal

单线程范围内数据共享使用ThreadLocal /** * @Description TODO * @author [email protected] * @since 2015年12月12日 * @version V1.0 */ public class DataShare { /** * ThreadLocal:保存和当前线程相关的变量数据 * 线程范围内的数据共享 ThreadLocal * 优雅的设计方式,Person * 每一个线程使用完毕要清空ThreadLocal保存的当前变量 *

JAVA多线程提高三:线程范围内共享变量&amp;ThreadLocal

今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据,就可以实现线程范围内共享相同的变量. 二.代码 Runnable中的run()方法里面执行Thread.currentThread()都会对应当前Runnable对应的线程,因此A.B中对应的Thread.currentThread

多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.JDK 1.2的版本中就提供java.lang.ThreadLocal,使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量. 1.下图和辅助代码解释ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据. 2.每个线程调用全局ThreadLocal

ThreadLocal实现:java线程范围内的共享数据,线程外独立

场景应用:银行转账时,A给B转账,C给D转账.两者都是调用的connection.begainTransaction();connection.commit(); 如何才能保证A,B同在一个线程中,C.D同在一个线程中,且A,BC,D在线程内共享,在线程外独立. 方法一,Map<Thread, Integer>实现: /** * 线程范围内的共享数据 */ package cn.itcast.lesson5; import java.util.HashMap; import java.util

ThreadLocal实现线程范围内共享变量

在web应用中,一个请求(带有请求参数)就是一个线程,那么如何区分哪些参数属于哪个线程呢?比如struts中,A用户登录,B用户也登录,那么在Action中怎么区分哪个是A用户的数据,哪个是B用户的数据.这就涉及到ThreadLocal类了,将变量与当前线程绑定.比如struts中,有一个容器类,那么A用户将数据放在A的容器中,B用户将数据放在B的容器中,然后再将容器与线程绑定,这样的话,A请求的线程处理A容器的数据,B请求的线程处理B容器的数据,而不会混淆. 示例如下: 1 package c

java线程:单例隐藏ThreadLocal实现线程数据共享

问题: 给定的二叉查找树中,有两个节点不小心被调换了位置,现在需要将其修正,不改变树的结构. 分析: 二叉排序树的中序遍历是有序的,所以这个问题又是建立在中序遍历模板上的问题,所以我们可以对其进行中序遍历,并用一个pre指针指向当前遍历结果中的最后一个结点,即下次遍历前的前一个结点.然后就可以通过将当前结点与pre结点进行比较,来判断是否有序了.若乱序,就将这两个结点都放入到预先定义的容器中. 错误的形式就这两种,我们看到,在乱序容器中,最多就存了四个元素,所以空间复杂度还是满足O(n)的,当然

多线程编程——线程同步方法

1.五种方式 1.1 synchronized同步方法 使用synchronized关键字修饰的方法.java每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法.在调用该方法前,需获取内置锁,否则就会处于阻塞状态. 如:public synchronized void save(){} 注:当synchronized关键字修饰静态方法时,会锁住整个类 1.2 synchronized同步代码块 即有synchronized关键字修饰的语句块.被该关键字修饰的语句块会自动被加上内