线程范围内的共享数据 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;
}


}




[size=medium]
ThreadLocal 是并发程序的一种解决方案,他利用空间换时间,也就是为每个线程分配空间来实现多线程,ThreadLocal 完全不使用锁,因此他不是一种数据共享的锁。
从性能上来说,ThreadLocal 并不具备绝对的优势,但是作为一种完全不使用锁的多线程解决方案,在高并发的情况下,在一定程度上可以减少锁的竞争。
[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

annan211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值