多线程05-线程范围内共享变量

1.问题引入

多个业务模块针对同一个static变量的操作 要保证在不同线程中 各模块操作的是自身对应的变量对象

例如:

package org.lkl.thead;

import java.util.Random;

/**
 * 线程共享数据
 * Function :
 * @author : Liaokailin
 * CreateDate : 2014-6-12
 * version : 1.0
 */
public class ThreadShareData {

    private static  int data = 0 ;

    public static void main(String[] args) {
      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()+ " put random data:"+data);
                new A().get() ;
                new B().get() ;
            }

        }).start() ;
      }

    }

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

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

  模块A ,B都需要访问static的变量data   在线程0中会随机生成一个data值 假设为10  那么此时模块A和模块B在线程0中得到的data的值为10 ;在线程1中 假设会为data赋值为20 那么在当前线程下

模块A和模块B得到data的值应该为20

看程序执行的结果:

Thread-0 put random data:-1344602819
Thread-1 put random data:-1842611697
A from Thread-1 get data :-1842611697
A from Thread-0 get data :-1842611697
B from Thread-1 get data :-1842611697
B from Thread-0 get data :-1842611697

在线程0中执行模块A和模块B的方法去获取data的值 但是在获取之前  线程1就将data的值给修改为-1842611697 导致线程0中的模块A和模块B获取了错误的数据.

2.解决问题

那么如何得到正确的效果呢?

可是将data数据和当前允许的线程绑定在一块,在模块A和模块B去获取数据data的时候 是通过当前所属的线程去取得data的结果就行了。

声明一个Map集合 集合的Key为Thread 存储当前所属线程 Value 保存data的值,代码如下:

package org.lkl.thead.sharedata;

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

/**
 * 线程共享数据
 * Function :
 * @author : Liaokailin
 * CreateDate : 2014-6-12
 * version : 1.0
 */
public class ThreadShareData {

    private static  int data = 0 ;
    private static Map<Thread,Integer> threadData = new HashMap<Thread, Integer>() ;

    public static void main(String[] args) {
      for(int i = 0 ;i<2 ;i++){
        new Thread(new Runnable(){

            @Override
            public  void run() {
                synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                    data = new Random().nextInt();
                    threadData.put(Thread.currentThread(), data) ; //将数据绑定到带当前线程
                    System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                    new A().get() ;
                    new B().get() ;
                }
            }

        }).start() ;
      }

    }

    static class A {
        public int get(){
            //取数据都从当前线程中取得
            int d = threadData.get(Thread.currentThread()) ;
            System.out.println("A from " + Thread.currentThread().getName()
                    + " get data :" + d);
            return data ;
        }
    }

    static class B{
        public int get(){
            //取数据都从当前线程中取得
            int d  = threadData.get(Thread.currentThread()) ;
            System.out.println("B from " + Thread.currentThread().getName()
                    + " get data :" + d);
            return data ;
        }
    }
}

程序执行的结果:

Thread-0 put random data:-1842492021
A from Thread-0 get data :-1842492021
B from Thread-0 get data :-1842492021
Thread-1 put random data:-1886142929
A from Thread-1 get data :-1886142929
B from Thread-1 get data :-1886142929

3 问题的拓展

将数据与当前线程相挂钩的话 那么显然是可以利用ThreadLocal这个类  那么改造上面的代码得到下面的代码:

package org.lkl.thead.sharedata;

import java.util.Random;

/**
 * 线程共享数据
 * Function :
 * @author : Liaokailin
 * CreateDate : 2014-6-12
 * version : 1.0
 */
public class ThreadShareData {

    private static  int data = 0 ;
    /**
     * ThreadLocal 类似于map集合 只是集合的key是当前正在运行的线程
     * 通过ThreadLocal可以将变量(或者是变量的容器)与当前线程相绑定.
     */
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() ;

    public static void main(String[] args) {
      for(int i = 0 ;i<2 ;i++){
        new Thread(new Runnable(){

            @Override
            public  void run() {
                synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                    data = new Random().nextInt();
                    threadLocal.set(data) ; //将数据绑定到带当前线程
                    System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                    new A().get() ;
                    new B().get() ;
                }
            }

        }).start() ;
      }

    }

    static class A {
        public int get(){
            //取数据都从当前线程中取得
            int d = threadLocal.get() ;
            System.out.println("A from " + Thread.currentThread().getName()
                    + " get data :" + d);
            return data ;
        }
    }

    static class B{
        public int get(){
            //取数据都从当前线程中取得
            int d = threadLocal.get() ;
            System.out.println("B from " + Thread.currentThread().getName()
                    + " get data :" + d);
            return data ;
        }
    }
}

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

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

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

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

package org.lkl.thead.sharedata;

import java.util.Random;

/**
 * 线程共享数据
 * Function :
 * @author : Liaokailin
 * CreateDate : 2014-6-12
 * version : 1.0
 */
public class ThreadShareData {

    private static  int data = 0 ;

    public static void main(String[] args) {
      for(int i = 0 ;i<2 ;i++){
        new Thread(new Runnable(){

            @Override
            public  void run() {
                synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                    data = new Random().nextInt();
                    VariableContainer.getThreadInstance().setAge(data) ;
                    VariableContainer.getThreadInstance().setName("liaokailin-"+data) ;
                    System.out.println(Thread.currentThread().getName()+ " Put  VariableContainer:"+VariableContainer.getThreadInstance().toString());
                    new A().get() ;
                    new B().get() ;
                }
            }

        }).start() ;
      }

    }

    static class A {
        public int get(){
            //取数据都从当前线程中取得
            System.out.println("A from " + Thread.currentThread().getName()
                    + " get data :" + VariableContainer.getThreadInstance().toString());
            return data ;
        }
    }

    static class B{
        public int get(){
            //取数据都从当前线程中取得 

            System.out.println("B from " + Thread.currentThread().getName()
                    + " get data :" + VariableContainer.getThreadInstance().toString());
            return data ;
        }
    }

}

/**
 * 变量容器
 */
class VariableContainer{
    private String name ;
    private int age ;
    private static  ThreadLocal<VariableContainer> threadLocal = new ThreadLocal<VariableContainer>() ;
    /**
     * 获取当前线程中绑定的变量容器   外部可以往容器中设值
     */
    public static VariableContainer  getThreadInstance(){
        VariableContainer container = threadLocal.get() ;
        if(container==null){
            container = new VariableContainer() ;
            threadLocal.set(container) ;
        }
        return container ;

    }

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

    @Override
    public String toString() {
        return "VariableContainer [name=" + name + ", age=" + age + "]";
    }

}

执行结果:

Thread-0 Put  VariableContainer:VariableContainer [name=liaokailin--2103275506, age=-2103275506]
A from Thread-0 get data :VariableContainer [name=liaokailin--2103275506, age=-2103275506]
B from Thread-0 get data :VariableContainer [name=liaokailin--2103275506, age=-2103275506]
Thread-1 Put  VariableContainer:VariableContainer [name=liaokailin-816504398, age=816504398]
A from Thread-1 get data :VariableContainer [name=liaokailin-816504398, age=816504398]
B from Thread-1 get data :VariableContainer [name=liaokailin-816504398, age=816504398]

多线程05-线程范围内共享变量

时间: 2024-11-07 17:44:27

多线程05-线程范围内共享变量的相关文章

(黑马Java多线程与并发库高级应用)05 线程范围内共享变量的概念与作用

如下代码中会出现问题 package cn.itcast.heima2; import java.util.HashMap; import java.util.Random; public class ThreadScopeShareData { private static int data = 0; private static HashMap<Thread, Integer> threadData = new HashMap<>(); public static void m

ThreadLocal实现线程范围内共享变量

在web应用中,一个请求(带有请求参数)就是一个线程,那么如何区分哪些参数属于哪个线程呢?比如struts中,A用户登录,B用户也登录,那么在Action中怎么区分哪个是A用户的数据,哪个是B用户的数据.这就涉及到ThreadLocal类了,将变量与当前线程绑定.比如struts中,有一个容器类,那么A用户将数据放在A的容器中,B用户将数据放在B的容器中,然后再将容器与线程绑定,这样的话,A请求的线程处理A容器的数据,B请求的线程处理B容器的数据,而不会混淆. 示例如下: 1 package c

线程范围内共享变量的概念与作用演进方式

/** * * @描述: 线程范围内共享变量的概念与作用 . * @作者: Wnj . * @创建时间: 2017年5月15日 . * @版本: 1.0 . */ public class ThreadLocalTest { private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); private static ThreadLocal<MyThreadScopeData> myThreadSc

线程范围内共享变量的概念与作用

package cn.itcast.heima2; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * * @描述: 线程范围内共享变量的概念与作用 . * @作者: Wnj . * @创建时间: 2017年5月15日 . * @版本: 1.0 . */ public class ThreadScopeShareData { // private static int data = 0; p

JAVA多线程提高三:线程范围内共享变量&amp;ThreadLocal

今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据,就可以实现线程范围内共享相同的变量. 二.代码 Runnable中的run()方法里面执行Thread.currentThread()都会对应当前Runnable对应的线程,因此A.B中对应的Thread.currentThread

多线程之线程范围内的数据共享ThreadLocal

如果多个线程使用同一个数据,那么如何保证线程范围内的数据共享. 我们可以使用一个map来存储当前线程,以及其数据如下: package andy.thread.traditional.test; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * @author Zhang,Tianyou * @version 2014年11月8日 下午2:12:44 */ // 线程范围内的共享数据 pu

5.线程范围内共享变量的概念与作用

1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.Random; 4 5 /******************************* 6 * 模拟ThrealLocal的实现 7 * 用处: 8 * 用在数据库操作中俄beginTransaction -> commit 9 * 在Hibernate中也是使用它来保证在多线程下Session自己不冲突. 10 * OpenSessionInView模

Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类

1.线程范围内共享变量 1.1 前奏: 使用一个Map来实现线程范围内共享变量 public class ThreadScopeShareData { static Map<Thread, Integer> dataMap = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public class Thread implements Runnable { /* What will be run. */ private Runnable target; ...... /** * Causes this thread to begin execution; the Java Virtu