java并发-多线程之线程范围内共享变量(5)
线程范围内共享变量要实现的效果为:
多个对象间共享同一线程内的变量
未实现线程共享变量的demo:
- package cn.itcast.heima2;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Random;
- public class ThreadScopeShareData {
- private static int data = 0;
- // private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
- public static void main(String[] args) {
- //共启动2个线程
- for(int i=0;i<2;i++){
- //启动一个线程
- new Thread(new Runnable(){
- @Override
- public void run() {
- data = new Random().nextInt();
- System.out.println(Thread.currentThread().getName()
- + " has put data :" + data);
- //以当前线程为key值放入到map中,当取值时根据各自的线程取各自的数据
- // 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);
- }
- }
- }
运行结果:
通过打印出的结果可以看出,当Thread-0获取了一个随机数,修改了data的值,正在睡眠的时候,Thread-1又获取了一个随机数,同样修改了data的值,然后Thread-1调用了静态内部类A和B的get方法,实际上此时的data已经是Thread-1拿到的随机数了。
当然,我们可以通过增加synchronized加锁来控制线程的运行。让Thread-0运行完方法之前,Thread-1不能修改data的值。
此外,还可以使用另外几种方法来获取线程运行时变量赋予的真正值。
线程范围内共享变量实现方式:
Map实现方式:
- package cn.itcast.heima2;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Random;
- public class ThreadScopeShareData {
- private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
- public static void main(String[] args) {
- //共启动2个线程
- 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);
- //以当前线程为key值放入到map中,当取值时根据各自的线程取各自的数据
- 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方式:
- package cn.itcast.heima2;
- import java.util.Random;
- public class ThreadLocalTest {
- 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);
- x.set(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);
- }
- }
- static class B{
- public void get(){
- int data = x.get();
- System.out.println("B from " + Thread.currentThread().getName()
- + " get data :" + data);
- }
- }
- }
存在的问题:一个ThreadLocal代表一个变量,故其中只能放一个数据,如果你有两个变量要线程范围内共享,则要定义两个ThreadLocal。如下为解决方案:
扩展方式-单例方式处理对象:
- package cn.itcast.heima2;
- import java.util.Random;
- public class ThreadLocalTest {
- // 方式一
- // private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
- private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
- 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);
- // 方式一 ThreadLocal
- // x.set(data);
- // 方式二 new对象方式,将多个属性放到对象中
- // MyThreadScopeData myData = new MyThreadScopeData();
- // myData.setName("name" + data);
- // myData.setAge(data);
- // myThreadScopeData.set(myData);
- // 方式三 使用单例模式
- MyThreadScopeData.getThreadInstance().setName("name" + data);
- MyThreadScopeData.getThreadInstance().setAge(data);
- new A().get();
- new B().get();
- }
- }).start();
- }
- }
- static class A{
- public void get(){
- // 方式一 ThreadLocal
- // int data = x.get();
- // System.out.println("A from " + Thread.currentThread().getName()
- // + " get data :" + data);
- // 方式二 new对象方式,将多个属性放到对象中
- // MyThreadScopeData myData = myThreadScopeData.get();;
- // System.out.println("A from " + Thread.currentThread().getName()
- // + " getMyData: " + myData.getName() + "," +
- // myData.getAge());
- // 方式三 使用单例模式
- MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
- System.out.println("A from " + Thread.currentThread().getName()
- + " getMyData: " + 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.get();;
- // System.out.println("B from " + Thread.currentThread().getName()
- // + " getMyData: " + myData.getName() + "," +
- // myData.getAge());
- MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
- System.out.println("B from " + Thread.currentThread().getName()
- + " getMyData: " + myData.getName() + "," +
- myData.getAge());
- }
- }
- }
- class MyThreadScopeData{
- private MyThreadScopeData(){}
- private static MyThreadScopeData instance = null;//new MyThreadScopeData();
- private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
- public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
- 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;
- }
- }
总结:
synchronized和使用ThreadLocal均可以解决以上的问题,只是这是两种不同的方式,synchronized是依赖锁的机制一个执行完后另一个再执行。ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。
ThreadLocal的应用:
在业务逻辑层需要调用多个Dao层的方法,我们要保证事务(jdbc事务)就要确保他们使用的是同一个数据库连接.那么如何确保使用同一个数据库连接呢?
相关推荐
- Java的多线程并发出现缓存一致性问题之拙见
- Java5线程并发库之同步集合
- Java多线程与并发库高级应用--19_传智播客_张孝祥_java5同步集合类的应用
- Java多线程与并发库高级应用--15_传智播客_张孝祥_java5的CyclicBarrier同步工具
- Java多线程与并发学习之(一):进程与线程
- JAVA多线程—共享变量的可见性问题
- java多线程系列翻译之java并发/多线程教程
- 资深消防猿为你解读Java多线程与并发模型之共享对象
- Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
- 多线程并发支撑基础之JAVA内存模型
- 多线程之实例变量与线程安全
- 多线程中的使用共享变量的问题