本文是传智博客多线程视频的学习笔记。

原版本见

http://blog.csdn.net/dlf123321/article/details/42531979

ThreadLocal是一个和线程安全相关的类。

它能干什么?

能保证在一个线程内,某个变量的全局共享。

说的很模糊,咱们看一个图

线程1里面的数据,应该在线程1范围内的模块a,b,c都能访问。

线程2里面的数据,应该在线程3范围内的模块a,b,c都能访问。

且线程1,2之间数据不会混淆。

那它有什么用呢?

举个例子,银行的转账包含两步,存款和取款,时候如果在存款取款中间出了问题,就得回滚;如果一切正常等整个交易完成了再commit,而调用commit的对象是Connection。那你说,如果多个线程共用一个Connection会发生什么问题?

一个非线程安全的例子

在我们讲述ThreadLocal之前,我们先看一个例子。

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadScopeDataShare {
    static private int data = 0;

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 2; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    int data2= new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName()+" put "+data2);
                    data=data2;
                    try {
                        Thread.sleep(1000); //为什么要睡1秒 大家懂吗?
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    new A().get();
                    new B().get();

                }
            });
        }
        threadPool.shutdown();
    }

    public static int getData() {
        return data;
    }

}

class A {
    public int get() {
        int data = ThreadScopeDataShare.getData();
        System.out
            .println("a  "+Thread.currentThread().getName() + " getdata " + data);
        return data;
    }
}

class B {
    public int get() {
        int data = ThreadScopeDataShare.getData();
        System.out
            .println("b  "+Thread.currentThread().getName() + " getdata " + data);
        return data;
    }
}

在我们设想中,应该是线程1放的数据,在线程1中,模块A与模块取得的数据应该是一致的。同理,线程2放的数据,与工作再线程2下的模块A模板取得的数据也应该是一致的。

可是上面的代码的运行结果却是:

pool-1-thread-1 put 90

pool-1-thread-2 put 78

a  pool-1-thread-2 getdata 78

b  pool-1-thread-2 getdata 78

a  pool-1-thread-1 getdata 78

b  pool-1-thread-1 getdata 78

改进版

我们新建一个map,key是当前线程,value是我们要保存的数据。

那么就可以保证每个线程的各个模块取得的数据都是一致的。

public class ThreadScopeShareData3 {

    private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 2; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    int data2= new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName()+" put "+data2);
                    threadData.put(Thread.currentThread(),data2);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    new A().get();
                    new B().get();

                }
            });
        }
        threadPool.shutdown();
    }

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

运行结果

pool-1-thread-1 put 2

pool-1-thread-2 put 99

A from pool-1-thread-2 get data :99

A from pool-1-thread-1 get data :2

B from pool-1-thread-1 get data :2

B from pool-1-thread-2 get data :99

ThreadLocal的简单介绍

  早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

  当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。



我们先看应用再讲原理,然后再讲一个实际的应用。

第一个应用

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(500);
                    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);
        }
    }
}

Thread-0 has put data :67

A from Thread-0 get data :67

B from Thread-0 get data :67

Thread-1 has put data :221

A from Thread-1 get data :221

B from Thread-1 get data :221

完全符合我们的要求。

这里有个问题:

如果一个线程能要共享多个变量怎么做?

private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();

private static ThreadLocal<Integer> y = new ThreadLocal<Integer>();

不嫌麻烦吗?

ThreadLocal里可以放Interger,也可以放Objcet么。

如果几个变量有关系,如name,age我们就把它们包装成User;

如果变量没有关系,那就包装成一个map。

(当然一个线程如果要共享多个变量,那么分别设置为x,y也是可以的)

这样可以不?

import java.util.Random;

public class ThreadLocalTest3 {

    private static ThreadLocal<MyThreadScopeData2> myThreadScopeData = new ThreadLocal<MyThreadScopeData2>();

    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(500);

                    MyThreadScopeData2 myData = new MyThreadScopeData2();
                    myData.setName("name" + data);
                    myData.setAge(data);
                    myThreadScopeData.set(myData);

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

    static class A {
        public void get() {
            MyThreadScopeData2 myData = myThreadScopeData.get();

            System.out
                    .println("A from " + Thread.currentThread().getName()
                            + " getMyData: " + myData.getName() + ","
                            + myData.getAge());
        }
    }

        //省略class B
}

class MyThreadScopeData2 {

    private static ThreadLocal<MyThreadScopeData2> map = new ThreadLocal<MyThreadScopeData2>();

    private String name;
    private int age;

    //省略get set
}

可以,不过对用户来说暴露了ThreadLocal的应用,我们希望在调用的时候,ThreadLocal对用户是透明的。

换句话说,我们得把ThreadLocal包装起来。

import java.util.Random;

public class ThreadLocalTest2 {

    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(500);

                    MyThreadScopeData.getThreadInstance().setName("name" + data);
                    MyThreadScopeData.getThreadInstance().setAge(data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }

    static class A{
        public void get(){

            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("A from " + Thread.currentThread().getName()
                    + " getMyData: " + myData.getName() + "," +
                    myData.getAge());
        }
    }
    //省略Class B
}

class MyThreadScopeData{
    private MyThreadScopeData(){}
    public static  MyThreadScopeData getThreadInstance(){
        MyThreadScopeData instance = map.get();
        if(instance == null){
            instance = new MyThreadScopeData();
            map.set(instance);
        }
        return instance;
    }

    private static ThreadLocal<MyThreadScopeData> map =
            new ThreadLocal<MyThreadScopeData>();

    private String name;
    private int age;
    //省略getset
}

关于上面的单例模式可以参考

http://blog.csdn.net/lovelion/article/details/7420886

现在重头戏来了,看看ThreadLocal实现的原理。

参见:

http://blog.csdn.net/dlf123321/article/details/42531979

ThreadLocal深入理解 修订版的更多相关文章

  1. ThreadLocal深入理解二

    转载:http://doc00.com/doc/101101jf6 今天在看之前转载的博客:ThreadLocal的内部实现原理.突然有个疑问, 按照threadLocal的原理, 当把一个对象存入到 ...

  2. ThreadLocal深入理解一

    转载:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使 ...

  3. Java中的ThreadLocal深入理解

    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...

  4. ThreadLocal的理解与应用场景分析

    对于Java ThreadLocal的理解与应用场景分析 一.对ThreadLocal理解 ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存 ...

  5. Python中ThreadLocal的理解与使用

    一.对 ThreadLocal 的理解 ThreadLocal,有的人叫它线程本地变量,也有的人叫它线程本地存储,其实意思一样. ThreadLocal 在每一个变量中都会创建一个副本,每个线程都可以 ...

  6. java中threadlocal的理解

    [TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...

  7. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  8. threadlocal彻底理解,深刻

    本文转自http://blog.csdn.net/huachao1001/article/details/51970237 ThreadLocal的使用相信大家都比较熟悉,但是ThreadLocal内 ...

  9. 我对ThreadLocal的理解

    声明:小弟菜狗一个.对ThreadLocal的描写叙述和理解难免有所偏差 近期由于须要深入的了解android的handler消息机制而去查看了Looper的源代码.众所周知在主线程中是不须要在程序猿 ...

随机推荐

  1. PHP+MySQL 分页那点事

    分页技术随处可见,这可以算得上是最为基础的网站功能了.于是今天尝试着用PHP来实现一个分页的小例子. 准备工作 环境准备 Apache MySQL PHP 工作环境 数据库准备 建库 建表 预存数据 ...

  2. Python 文本转语音

    文本转语音,一般会用在无障碍开发.下面介绍如何使用Python实现将文本文件转换成语音输出. 准备 使用Speech API 原理 示例代码 小总结 pyttsx方式 原理 示例代码 小总结 pytt ...

  3. VBA find方法

    Sub Sample() Dim sfzs As New Collection Dim ws, wbs, dbs As Worksheet Dim r As Long Set ws = ThisWor ...

  4. Ribbon WorkBench 当ValueRule的值为空时的设置

    在定制Ribbon按钮的规则的时候,有时需要根据某个字段值是否为空不设定Ribbon按钮的Display rules或Enable Rules,根据Crm的版本的不同,设置有所差别: 对于Dynami ...

  5. Xcode7.3.1中通过最新的CocoaPod安装pop动画引擎

    CocoaPod是一个用ruby实现,用于方便的管理Xcode中第三方插件的管理器.用它我们可以很方便的安装和升级插件而不用担心破坏原有的项目. 而pop是一个用于实现App中动画的引擎,它是由Fac ...

  6. openJdk和sun Jdk区别和安装

    openJdk和sun jdk的区别 使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SU ...

  7. PhysicsJoint

    1 PhysicsJoint的使用 T09Join.h #ifndef__T09Joint_H__ #define__T09Joint_H__ #include"T32.h" cl ...

  8. Afinal加载网络图片及下载文件使用方法

    Afinal快速开发框架使用起来非常方便,下面将讲解如何利用Afinal加载网络图片及下载文件: 先看效果图: 注意:使用Afinal前需添加Afinal的jar,可以在这里下载:http://dow ...

  9. [struts2学习笔记] 第六节 struts2依赖的jar包还有Could not find action or result 错误解决

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/43272061 本文作者:sushengmiyan ------------------ ...

  10. Hadoop:Hadoop简介及环境配置

    http://blog.csdn.net/pipisorry/article/details/51243805 Hadoop简介 下次写上... 皮皮blog 配置hadoop环境可能出现的问题 每次 ...