`
annan211
  • 浏览: 456546 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

线程范围内的共享数据 ThreadLocal 分析与详解

阅读更多
Java 线程范围内的数据共享机制,需要解决的问题是  : 多个业务模块针对同一个static变量的操作 要保证在不同线程中 各模块操作的是自身对应的变量对象


实例  A  B 两个类 需要操作同一个static变量 data


初步代码 可能如下:

  package threadLocal;

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

/**
 * 线程范围内的共享数据
 * @author jingfn
 *
 */
public class ThreadScopeShareDate {
	private static int data ;
	public static void main(String[] args) {
		for(int i=0;i<2;i++)
		new Thread(new Runnable(){
			public  void run(){
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+"--put data "+data);
					new A().get();
					new B().get();
				}
		}).start();
	}

	static class A{
		private void get(){
			System.out.println("A--get data from "+Thread.currentThread().getName()+" of "+data);
		}
	}
	static class B{
		private void get(){
			System.out.println("B--get data from "+Thread.currentThread().getName()+" of "+data);
		}
	}
}



执行结果:
  Thread-0--put data 350455289
Thread-1--put data -1313940209
A--get data from Thread-0 of -1313940209
A--get data from Thread-1 of -1313940209
B--get data from Thread-1 of -1313940209
B--get data from Thread-0 of -1313940209


分析结果可以看出 线程0 赋值数据  350455289,线程1 赋值数据  -1313940209,模块AB 拿到的data  却只是线程1 赋值的结果,这就说明在线程 0 赋值之后,AB操作之前,线程1 就已经将data的值修改为  -1313940209 了。


简单一点的修改 可以如下:

  package threadLocal;

import java.util.Random;

/**
 * 线程范围内的共享数据
 * @author jingfn
 *
 */
public class ThreadScopeShareDate {
	private static int data ;
	public static void main(String[] args) {
		for(int i=0;i<2;i++)
		new Thread(new Runnable(){
			public  void run(){
				synchronized(ThreadScopeShareDate.class){
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+"--put data "+data);
					new A().get();
					new B().get();
				}
			}
		}).start();
	}

	static class A{
		private void get(){
			System.out.println("A--get data from "+Thread.currentThread().getName()+" of "+data);
		}
	}
	static class B{
		private void get(){
			System.out.println("B--get data from "+Thread.currentThread().getName()+" of "+data);
		}
	}
}



结果如下 
Thread-0--put data -30716152
A--get data from Thread-0 of -30716152
B--get data from Thread-0 of -30716152
Thread-1--put data 913737008
A--get data from Thread-1 of 913737008
B--get data from Thread-1 of 913737008


将数据与当前线程相挂钩的话 那么显然是可以利用ThreadLocal这个类
ThreadLocal 就相当于一个 Map,key就是当前线程,value为当前线程对应的值, 那么改造上面的代码得到下面的代码:
 package threadLocal;

import java.util.Random;

/**
 * 线程范围内的共享数据
 * @author jingfn
 *
 */
public class ThreadScopeShareDate {
	private static int data ;
	private static ThreadLocal<Integer> localdata = new ThreadLocal<Integer>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++)
		new Thread(new Runnable(){
			public  void run(){
				synchronized(ThreadScopeShareDate.class){
					data = new Random().nextInt();
					localdata.set(data);
					System.out.println(Thread.currentThread().getName()+"--put data "+data);
					new A().get();
					new B().get();
				}
			}
		}).start();
	}

	static class A{
		private void get(){
			System.out.println("A--get data from "+Thread.currentThread().getName()+" of "+localdata.get());
		}
	}
	static class B{
		private void get(){
			System.out.println("B--get data from "+Thread.currentThread().getName()+" of "+localdata.get());
		}
	}
}






代码执行完以后会得到同样的效果。

        接下来还有一个问题  上面一直讨论的是线程间共享一个变量 那么如果是多个变量呢?

        要直接ThreadLocal每次只能是一个变量和当前线程挂钩 如果有多个变量要和当前线程挂钩的话 就得生命多个ThreadLocal对象 这样子做很显然是不合理的。那如何处理呢?

        由于ThreadLocal可以并且只能和一个变量挂钩,那么我们可以将这个变量设置为一个变量的容器,容器中可以存在多个变量,这也就是将需要和当前线程绑定的变量封装到一个实体类中



package threadLocal;

import java.util.Random;

/**
 * 线程范围内的共享数据
 * @author jingfn
 *
 */
public class ThreadScopeShareDateContainer {
	private static int data ;
	public static void main(String[] args) {
		for(int i=0;i<2;i++)
		new Thread(new Runnable(){
			public  void run(){
				synchronized(ThreadScopeShareDateContainer.class){
					data = new Random().nextInt();
					Container.getInstance().setData1(data);
					Container.getInstance().setData2(data);
					System.out.println(Thread.currentThread().getName()+"--put data "+data);
					new A().get();
					new B().get();
				}
			}
		}).start();
	}

	static class A{
		private void get(){
			System.out.println("A--get data from "+Thread.currentThread().getName()+" of "+Container.getInstance().getData1()+"--"+Container.getInstance().getData2());
		}
	}
	static class B{
		private void get(){
			System.out.println("B--get data from "+Thread.currentThread().getName()+" of "+Container.getInstance().getData1()+"--"+Container.getInstance().getData2());
		}
	}
}

 class Container{
	 private int data1,data2;
	 private static ThreadLocal<Container> local = new ThreadLocal<Container>();
	 public static Container getInstance(){
		 Container container = local.get();;
		 if(container == null){
			 container = new Container();
			 local.set(container);
		 }
		 return container;
	 }
	public int getData1() {
		return data1;
	}
	public void setData1(int data1) {
		this.data1 = data1;
	}
	public int getData2() {
		return data2;
	}
	public void setData2(int data2) {
		this.data2 = data2;
	}


 }





  ThreadLocal 是并发程序的一种解决方案,他利用空间换时间,也就是为每个线程分配空间来实现多线程,ThreadLocal 完全不使用锁,因此他不是一种数据共享的锁。
从性能上来说,ThreadLocal 并不具备绝对的优势,但是作为一种完全不使用锁的多线程解决方案,在高并发的情况下,在一定程度上可以减少锁的竞争。
分享到:
评论

相关推荐

    张孝祥Java多线程与并发库高级应用笔记

    - **概念**:线程范围内共享变量是指在线程间共享的数据,需小心处理以避免竞态条件和数据不一致性。 - **作用**:允许线程间通信和协作,但需采用适当的同步机制(如`synchronized`关键字、`Lock`接口)以确保数据...

    JAVA多线程编程详解-详细操作例子.doc

    为了避免多个线程同时访问共享资源造成的数据不一致,Java 提供了多种同步机制,包括: 1. synchronized 关键字:可以修饰方法或代码块,确保同一时间只有一个线程可以执行特定的代码。 2. volatile 关键字:保证了...

    Mybatis多线程下如何使用Example详解

    `Example`类提供了丰富的API来构建复杂的查询条件,如 `createCriteria()` 用于创建一个新的 Criteria 实例,`and...Between(...)` 用于添加年龄在给定范围内的条件,以及 `clear()` 用于清除所有已设置的条件。...

    Java 多线程与并发(12-26)-JUC锁- ReentrantReadWriteLock详解.pdf

    当多个线程只需要读取共享数据时,它们可以同时持有读锁,而不会阻塞彼此。只有在写操作发生时,才会阻止其他读或写操作。 - 可重入性:持有读锁或写锁的线程可以再次获取同一类型的锁,而不会导致死锁。 - 读写锁...

    Servlet线程安全问题.docx

    2. **避免使用实例变量**:尽可能将状态信息存储在请求或会话范围内,这样每个请求或会话都有自己独立的数据,不会互相干扰。 3. **ThreadLocal变量**:对于需要在多线程环境中保持独立状态的变量,可以使用`...

    Java多线程

    线程总是属于某个进程,并且同一进程内的多个线程共享该进程的内存资源。 **2. Java中的线程** 在Java中,“线程”主要涉及两个方面: - **java.lang.Thread类的实例**:表示线程对象,拥有自己的变量和方法,...

    java 并发编程 多线程

    ### Java并发编程与多线程知识点详解 #### 1. 线程安全与锁定机制 - **确保线程安全**: 在Java并发编程中,确保线程安全是至关重要的。通常有三种方法来实现这一点: - 使用`synchronized`关键字:这是最基本的...

    针对于java面试资料.docx

    - `ThreadLocal` 不适用于解决多个线程间的共享数据问题,主要用于在线程内部保存数据。 - 在使用 `ThreadLocal` 时需注意显式地清理不再使用的变量,尤其是当线程可能重用时。 #### 三、MVCC(多版本并发控制) *...

    java加强笔记

    线程范围内共享变量的概念与作用 - 线程范围内的共享变量是指多个线程可以访问同一个变量,需要注意线程安全。 ##### 6. ThreadLocal类及应用技巧 - `ThreadLocal`类提供了一种线程局部变量的解决方案,可以为每个...

    java虚拟机并发编程

    - **线程本地存储(ThreadLocal)**:通过`ThreadLocal`类,可以为每个线程提供独立的变量副本,避免了线程间的共享数据竞争。 #### 四、Java并发编程的最佳实践 为了有效地利用Java的并发特性,开发人员应该遵循...

    java高并发编程推荐超好的一本电子书

    `ThreadLocal`为每个使用该变量的线程提供独立的副本,线程之间不会相互干扰,从而解决了多线程之间的数据共享问题。 #### 3. 避免过度同步 过度同步会导致性能下降,因此在设计时应尽量减少同步操作。例如,可以...

    Web服务器的工作原理

    **ServletContext**是一个全局共享的对象,用于存储应用程序范围内的数据。它是在Servlet容器启动时创建的,并在整个应用的生命周期内一直存在。`ServletContext`的主要用途有: - 存储全局属性和初始化参数。 - ...

    Java进阶知识点汇总.pdf

    - **volatile**:用于修饰共享变量,确保多线程环境中的可见性和有序性,但不保证原子性。 - **synchronized**:用于实现同步控制,可以修饰方法或代码块,保证同一时间只有一个线程执行该段代码。 - **import**:...

    Java面试题和答案.pdf

    ### Java高频面试知识点详解 #### 一、Java基础 **1. JDK和JRE有什么区别?** - **JRE(Java Runtime Environment)**: 包含Java虚拟机(JVM)、Java核心类库和支持文件,是运行Java程序所需的基础环境。 - **JDK...

    JSP面试题--基础

    - **getAttribute()**:用于获取请求范围内存储的任意属性。 #### 26. Cookie 的获取与设置 - **获取 Cookie**:通过 HttpServletRequest 对象的 `getCookies()` 方法。 - **设置 Cookie**:通过 ...

    java面试知识

    - **page**:当前页面范围内有效。 - **request**:客户端一次请求有效。 - **session**:用户会话期间有效。 - **application**:整个Web应用有效。 ##### List, Set, Collection, Collections - **Collection**...

    JAVA核心知识点整理(有效)

    2.2.4. 堆(Heap-线程共享)-运行时数据区 ...................................................................................... 23 2.2.5. 方法区/永久代(线程共享) ............................................

Global site tag (gtag.js) - Google Analytics