您的位置:首页 > 其它

线程范围内共享数据

2017-12-04 01:04 246 查看

1.线程范围内共享数据示意图



2.ThreadLocal实现线程范围的共享变量

用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据

我们可以先用所学知识来写一个

public class ThreadScopeShareData {

private static  int data = 0;
public static void main(String[] args) {

for(int i=0;i<6;i++) {
new Thread(new Runnable() {
@Override
public void run() {
data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data: " + data);

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

static class A{

public void get(){
System.out.println("A from " + Thread.currentThread().getName()+" get data: " + data);
}
}

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


如果光像上面这样写的话,那毫无疑问,肯定是有问题的,如下图所示并没有实现线程共享



此时,或许很多人就会想到用Map,key为当前线程,value为当前共享的值,这是一个好的思路

public class ThreadScopeShareData {

private static Map<Thread,Integer> threadData = new HashMap<Thread,Integer>();
public static void main(String[] args) {

for(int i=0;i<6;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);
new A().get();
new B().get();
}
}).start();
}
}

static class A{

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

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




此时就实现线程内共享数据了

其实对于ThreadLocal来说,它就是一个Map,它的key已经默认为Thread,而你只需要设定它的value值即可使用

用ThreadLocal实现如下

public class ThreadScopeShareData {

private static ThreadLocal<Integer> threadData = new ThreadLocal<Integer>();
public static void main(String[] args) {

for(int i=0;i<6;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.set(data);
new A().get();
new B().get();
}
}).start();
}
}

static class A{

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

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


3.ThreadLocal的应用场景

A.订单处理包含一系列操作

减少库存量,增加一条流水台账,修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中

B.银行转账包含一系列操作

把转出账户的月减少,把转入账户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的账户对象的方法

C.例如Struts2的ActionContext

同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个

4.对ThreadLocal的封装

实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量

对基本类型的数据的封装,这种应用相对很少见

对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象

总结

一个ThreadLocal代表一个变量,故其中里只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象.如果有一百个变量要线程共享呢?请先定义一个对象来装这一百个变量,然后在ThreadLocal中存储这一对象

你首先就会想要把这一百个数据封装成一个对象

public class ThreadLocalTest {

private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
private static  ThreadLocal<MyThreadScopeData> myThreadScopeDataThreadLocal = new ThreadLocal<MyThreadScopeData>();
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data: " + data);
x.set(data);
MyThreadScopeData myData = new MyThreadScopeData();
myData.setName("name"+data);
myData.setAge(data);
myThreadScopeDataThreadLocal.set(myData);
new A().get();
new B().get();

}
}).start();
}
}

static class A{
public void get(){
int data =  x.get();
System.out.println("A from " + Thread.currentThread().getName()+" get data: " + data);
MyThreadScopeData myData = myThreadScopeDataThreadLocal.get();
System.out.println("A from " + Thread.currentThread().getName()
+" getData:" + myData.getName()+", " + myData.getAge());
}
}

static  class B{
public void get(){
int data = x.get();
System.out.println("B from " + Thread.currentThread().getName()+" get data: " + data);
MyThreadScopeData myData = myThreadScopeDataThreadLocal.get();
System.out.println("B from " + Thread.currentThread().getName()
+" getData:" + myData.getName()+", " + myData.getAge());
}
}
}

class MyThreadScopeData{

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;
}
}


不过思路挺乱的呀,下面有更加优雅的方式

巧妙的采用单例模式

public class ThreadLocalTest {

private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
private static  ThreadLocal<MyThreadScopeData> myThreadScopeDataThreadLocal = new ThreadLocal<MyThreadScopeData>();
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data: " + data);
x.set(data);
MyThreadScopeData.getThreadInstance().setName("name"+data);//拿到个与本线程有关的实例对象
MyThreadScopeData.getThreadInstance().setAge(data);
new A().get();
new B().get();

}
}).start();
}
}

static class A{
public void get(){
int data =  x.get();
System.out.println("A from " + Thread.currentThread().getName()+" get data: " + data);
MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName()
+" getData:" + myData.getName()+", " + myData.getAge());
}
}

static  class B{
public void get(){
int data = x.get();
System.out.println("B from " + Thread.currentThread().getName()+" get data: " + data);
MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName()
+" getData:" + myData.getName()+", " + myData.getAge());
}
}
}

class MyThreadScopeData{//线程数据共享实例设计

private MyThreadScopeData(){}

public static /*synchronized*/ MyThreadScopeData getThreadInstance(){//单例模式
MyThreadScopeData instance = map.get();//此时不需要加互斥
if(instance==null){
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
//饥汉模式如果在多线程模式下,必须方法上加上互斥
/*if(instance == null){
instance = new MyThreadScopeData();
}
return instance;*/
}

private static  MyThreadScopeData instance = new MyThreadScopeData();//别人还没调用instance,它就已经new出来了

//private static  MyThreadScopeData instance = null;饥汉模式

private static  ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: