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

原版本见

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. springMVC源码分析--HandlerAdapter(一)

    HandlerAdapter的功能实际就是执行我们的具体的Controller.Servlet或者HttpRequestHandler中的方法. 类结构如下:

  2. EBS业务学习之库存管理

    库存管理业务流程 企业结构 库存结构 库存结构定义 指定每个子库存的特性: •子库存的数量跟踪 •资产类子库存 •保留子库存 •净值子库存 •包含在有效承诺中Include in ATP •子库存级库 ...

  3. cassandra eclipse 环境构建

    摘要 本文主要介绍如何在eclipse中搭建cassandra环境 更多cassandra,nosql 相关知识请访问http://www.webpersonaldeveloper.cn 正文 1.f ...

  4. Java提升篇之反射的原理(二)

    Java提升篇之通过反射越过泛型检查 /* *问题:在一个ArrayList<Integer>对象中,在这个集合中添加一个字符串. */ 在我们还没有学反射前,遇到这个问题都是无法实现的, ...

  5. 【移动开发】EditText输入字数限制总结(包括中文输入内存溢出的解决方法)

    限定EditText输入个数的解决方案很多,但是一般主要考虑两点,也就是处理两件事:(1)不同语言字符(英文.中文等)处理方式(2)输入字符达到数目后,是否仍然允许用户输入 第一点,涉及的东东其实蛮多 ...

  6. Android简易实战教程--第十九话《手把手教您监听EditText文本变化,实现抖动和震动的效果》

    昨晚写博客太仓促,代码结构有问题,早上测试发现没法监听文本变化!今日更改一下.真心见谅啦,哈哈!主活动的代码已经改好了,看截图这次的确实现了文本监听变化情况. 监听文本输入情况,仅仅限于土司略显low ...

  7. Calling LoadLibraryEx on ISAPI filter failed

    今天在访问IIS下的站点时莫名奇妙的遇到这个问题Calling LoadLibraryEx on ISAPI filter"C://..."  failed,前面引号中的" ...

  8. 插件前奏-android黑科技 hook介绍

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52091833 Android hook相关学习 参考:http://www.cydia ...

  9. Servlet读取文件的最好的方式

    在java web 开发的时候不可避免的会读取文本信息,但是方式不同,所付出的代价也是不一样的,今天学到了一个比较好的实用性的技巧,拿来与大家分享一下. 读取属性配置文件 之所以说成是读取属性(pro ...

  10. 【原创】Eclipse vs. IDEA快捷键对比大全

    花了一天时间熟悉IDEA的各种操作,将各种快捷键都试了一下,感觉很是不错!于是就整理了一下我经常用的一些Eclipse快捷键与IDEA的对比,方便像我一样使用Eclipse多年但想尝试些改变的同学们. ...