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

今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用。

一、概念

可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据,就可以实现线程范围内共享相同的变量。

二、代码

Runnable中的run()方法里面执行Thread.currentThread()都会对应当前Runnable对应的线程,因此A、B中对应的Thread.currentThread()对应所在的Runnable对应的线程

public class ThreadScopeShareData {

    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() {
                    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

JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于前面中的Map(内部并不是Map),也就是让每个线程拥有自己的值
一个ThreadLocal对象只能记录一个线程内部的一个共享变量,需要记录多个共享数据,可以创建多个ThreadLocal对象,或者将这些数据进行封装,将封装后的数据对象存入ThreadLocal对象中。
线程结束后也可以自动释放相关的ThreadLocal变量,也可以调用ThreadLocal.remove()方法用来更快释放内存。

代码:

public class ThreadLocalTest {  

    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() {
                    //创建每个线程私有的变量
                    int data = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName()+" has put data: "+data);
                    //往local里面设置值
                    threadLocal.set(data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }  

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

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

假设需要保存不止一个值,可以把其他属性的值打包成一个类,然后将该类设置成ThreadLocal的值。

下面代码中,在类MyThreadLocalScopeDate里面定义了一个静态变量Map,用来保存所有线程创建的MyThreadLocalScopeDate,并使用单例使得不管多少线程都只创建一个MyThreadLocalScopeDate对象。

public class ThreadLocalTest {  

    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() {
                    //创建每个线程私有的变量
                    int data = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName()+" has put data: "+data);
                    //往local里面设置值
                    threadLocal.set(data);
                    //获取自己线程的MyThreadLocalScopeDate实例对象
                    MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
                    myData.setName("name"+data);
                    myData.setAge(data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }  

    static class A{
        public void get(){
            int data =threadLocal.get();
            System.out.println("A from "+Thread.currentThread().getName()+" has get data: "+data);
            MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
            System.out.println("A from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
        }
    }  

    static class B{
        public void get(){
            int data =threadLocal.get();
            System.out.println("B from "+Thread.currentThread().getName()+" has get data: "+data);
            MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
            System.out.println("B from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
        }
    }
}  

class MyThreadLocalScopeDate{//单例模式  

    private MyThreadLocalScopeDate(){};//构造方法私有化
    private static ThreadLocal<MyThreadLocalScopeDate> map = new ThreadLocal<MyThreadLocalScopeDate>();//封装MyThreadLocalScopeDate是线程实现范围内共享  

    //思考AB两个线程过来的情况 自己分析 AB都需要的自己的对象 没有关系 所以不需要同步 如果有关系就需要同步了
    public static /*synchronized*/MyThreadLocalScopeDate getThreadInstance(){
        MyThreadLocalScopeDate instance =map.get();
        if(instance==null){
            instance = new MyThreadLocalScopeDate();
            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;
    }
}

参考资料:

《多线程视频》张孝祥

原文地址:https://www.cnblogs.com/pony1223/p/9251696.html

时间: 2024-08-08 04:36:15

JAVA多线程提高三:线程范围内共享变量&ThreadLocal的相关文章

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

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多线程及JDK5线程工具类总结

内容摘抄来自:传智播客 张孝祥 老师的<Java高新技术>视频,   并加入了个人总结和理解. 虽然我没有参加过任何培训班,但我很大一部分知识都来自于传智播客的网络分享视频. 十分真挚的感谢张老师的公开视频. 1.传统线程技术的回顾 1 import org.junit.Test; 2 /** 3 * 传统线程回顾 4 * 多线程不一定会提高运行效率,和CPU设计和架构有关. 5 * 多线程下载是抢了服务器的资源,并不是自身的机器加快. 6 * @author LiTaiQing 7 */ 8

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

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

Java多线程之后台线程不执行finally

后台线程不执行finally package wzh.daemon; import java.util.concurrent.TimeUnit; class ADaemon implements Runnable { @Override public void run() { try { System.out.println("Starting ADaemon"); TimeUnit.SECONDS.sleep(1); } catch (Exception e) { System.ou

Java多线程之后台线程

将线程设置成后台线程Daemons 主线程结果后,后台线程将自动结果. package wzh.test; import java.util.concurrent.TimeUnit; class SimpleDaemons implements Runnable{ @Override public void run() { try { while (true) { TimeUnit.MILLISECONDS.sleep(100); System.out.println(Thread.curren

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

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

Java多线程总结之线程安全队列Queue

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列. 注:什么叫线程安全?这个首先要明确.线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的.如果由于多线程的访问(比如修改.遍历.查看)而使这些变量结构被破坏

Java多线程-新特性-线程池

Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤为重要. 有关Java5线程新特征的内容全部在java.util.concurrent下面,里面包含数目众多的接口和类,熟悉这部分API特征是一项艰难的学习过程.目前有关这方面的资料和书籍都少之又少,大部分介绍线程方面书籍还停留在java5之前的知识层面上. 在Java5之

Java多线程系列--“JUC线程池”01之 线程池架构

概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构图线程池示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509903.html 线程池架构图 线程池的架构图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行