黑马程序员---java中如何实现线程范围内共享数据
2015-06-29 12:09
429 查看
——- android培训、java培训、期待与您交流! ———-
在Java多线程程序中,我们有时需要实现数据只在线程范围内有效,也就是说一个线程有一份数据。关于这个需求我们可以使用map集合来完成:
思路如下:
1,定义一个全局的Final map集合,集合中存入线程对象和线程对应的数据。
2,每次线程运行时用自己的对象存取自己的数据。
上面的思路用代码实现完全没有问题,可是Java
api已经为我们提供了一个map的代替类,这个类就像map的功能一样,但它使用起来跟方便而且专门用来处理有关线程范围内共享数据的问题,它就是java.lang.ThreadLocal。
ThreadLocal中使用最多的就是下面两个方法
1,T get(): 返回当前线程对应的线程本地变量
2,void set(T value):设置当前线程的线程本地变量的值
问题来了:如果一个线程需要存取多个数据呢?
很多人马上想到可以用一个对象来封装,然后get和set的时候使用对象就可以了。当然这个方法不是不可以,但是这种方式很乱写起来很费力。那既然我们学的是面向对象的思想为何不重新考虑把存取线程范围内的数据封装到数据本身呢!
我们的思路是这样的:我们把数据封装到一个类里面,把它叫做数据类。数据类本身就提供了线程本地化,不同的线程使用数据的时候就可以得到和这个线程对应的数据。线程本身不必去维护存取ThreadLocal变量,这样的代码看起来就很简洁,优雅。并且封装之后大大提高了代码的可重用性。
下面我们用代码实现这种优雅的设计:
在Java多线程程序中,我们有时需要实现数据只在线程范围内有效,也就是说一个线程有一份数据。关于这个需求我们可以使用map集合来完成:
思路如下:
1,定义一个全局的Final map集合,集合中存入线程对象和线程对应的数据。
2,每次线程运行时用自己的对象存取自己的数据。
上面的思路用代码实现完全没有问题,可是Java
api已经为我们提供了一个map的代替类,这个类就像map的功能一样,但它使用起来跟方便而且专门用来处理有关线程范围内共享数据的问题,它就是java.lang.ThreadLocal。
ThreadLocal中使用最多的就是下面两个方法
1,T get(): 返回当前线程对应的线程本地变量
2,void set(T value):设置当前线程的线程本地变量的值
问题来了:如果一个线程需要存取多个数据呢?
很多人马上想到可以用一个对象来封装,然后get和set的时候使用对象就可以了。当然这个方法不是不可以,但是这种方式很乱写起来很费力。那既然我们学的是面向对象的思想为何不重新考虑把存取线程范围内的数据封装到数据本身呢!
我们的思路是这样的:我们把数据封装到一个类里面,把它叫做数据类。数据类本身就提供了线程本地化,不同的线程使用数据的时候就可以得到和这个线程对应的数据。线程本身不必去维护存取ThreadLocal变量,这样的代码看起来就很简洁,优雅。并且封装之后大大提高了代码的可重用性。
下面我们用代码实现这种优雅的设计:
package com.test; import java.util.Random; public class ThreadLocalTest { public static void main(String[] args)//主函数,启动两百个线程,每个线程首先设置自己的数据,然后分别调用A模块和B模块取数据 { for (int i = 0; i < 200; i++)//产生200个线程。用来验证线程不会产生数据不一致现象 { new Thread(new Runnable()//产生线程 { @Override public void run() { int x = new Random().nextInt();//产生一个随机数。 MyThreadScopeData.getInstance().setName("name" + x);//调用一个数据类产生实例,并设置数据。这里获取到的实例都是独立于每一个线程的,也就是说。线程取到的数据只属于本线程。 MyThreadScopeData.getInstance().setAge(x);//同上 System.out.println(Thread.currentThread().getName() + " 线程放入了 -->" + MyThreadScopeData.getInstance().getName() + "-->" + MyThreadScopeData.getInstance().getAge());//打印放入的数据 new A().get();//调用A模块读取数据。A,B模块也在这个线程中。 new B().get();//同上 } }).start();//启动线程 } } static class A//模拟模块A { public void get() { MyThreadScopeData x = MyThreadScopeData.getInstance();//取数据。取到的数据和当前线程对应 System.out.println("A模块 " + Thread.currentThread().getName() + "线程取到:Name:" + x.getName() + "-->Age:" + x.getAge());//打印取到的数据 } } static class B { public void get() { MyThreadScopeData x = MyThreadScopeData.getInstance(); System.out.println("B模块 " + Thread.currentThread().getName() + "线程取到:Name:" + x.getName() + "-->Age:" + x.getAge()); } } } class MyThreadScopeData//这就是封装数据的类。这个类是可复用的。这个类的设计类似于单例设计模式,但它并不是单例设计模式。因为它要保证每一个调用线程都有一份数据,所以它并不是单例的。这个类要被多个线程共享使用,所以getInstance方法必须被同步,否则会出现数据不一致现象。 { // Field private String name;//姓名 private int age;//年龄 // Static Filed private static MyThreadScopeData instance = null;//引用每个线程的实例 private final static ThreadLocal<MyThreadScopeData> var = new ThreadLocal<MyThreadScopeData>();//ThreadLocal变量存储所有线程对应的数据,最好声明成final类型以防止无意修改。 // Constructor private MyThreadScopeData()//私有化构造器用来阻止用户用New 实例化对象。因为我们不允许实例化对象,如何实例化对象已经被类封装了。也就是说类自己管理如何实例化对象,外界不必操心。 { } public static MyThreadScopeData getInstance()//这个方法用来返回数据实体。它首先会从ThreadLocal里面取数据,如果取到的数据时空,那么就说明还没有数据,所以就new 一个实例并存到里面然后返回这个实例。如果取出的不是空那么就返回这个对象。 { synchronized (MyThreadScopeData.class)// 因为是多线程操作这个代码所以要加同步 { instance = var.get(); if (instance == null) { instance = new MyThreadScopeData(); var.set(instance); } return instance; } } // getters and setters 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; } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统