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

原版本见

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. (译)Objective-C 类属性

    翻译自:Objective-C Class Properties 译者:Haley_Wong 由于Swift 3.0 出了太多令人兴奋的新特性,人们很容易忽略 Objective-C中的小改动.苹果展 ...

  2. 递归的神奇之处在于你会发现问题竟然解决了--解N皇后谜题有感

    看sicp看到8皇后谜题, 突然兴致来了,尝试独立解决(scheme代码的好处在于,即使你瞟了眼答案, 也不会有任何收获, 除了知道那儿有一坨神秘的括号和英文字符外但Python代码就不同了),成功了 ...

  3. Spring入门介绍-AOP(三)

    AOP的概念 AOP是面向切面编程的缩写,它是一种编程的新思想.对我们经常提起的oop(面对对象编程)有一定的联系. AOP和OOP的关系 AOP可以说是oop的某一方便的补充,oop侧重于对静态的属 ...

  4. 04_关于元数据,ResultSetMetaData对象以及API方法介绍

     ResultSetMetaData对象 元数据,可以理解为数据的数据 Jdbc中的元数据是指数据库.表.列的定义信息. ResultSetMetaData对象表示结果集ResultSet对象的元 ...

  5. android连接打印机

    android连接  网络打印,主要使用socket连接设备,发送指令给设备. 首先要有设备的IP,端口号一般默认的是9100 //打印设备网络IP etIp.setText("192.16 ...

  6. 【问题汇总】ScrollView嵌套GridView的问题

    在开发中遇到了ScrollView嵌套GridView的情况,由于这两款控件都自带滚动条,当它们碰到一起的时候便会出问题,即GridView会显示不全. 解决办法,自定义一个GridView控件. [ ...

  7. UNIX网络编程——select函数的并发限制和 poll 函数应用举例

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置,  ...

  8. linux service 简单易懂贴

    service用于管理Linux操作系统中服务的命令 1.不是在所有linux发行版本中都有.主要是在redhat.fedora.mandriva和centos中. 2. 命令位于/sbin目录下,用 ...

  9. erMaster插件

    需求: 在做开源项目时,了解基本业务后.试图从数据库表设计来分析项目.通过visio时绘制操作繁琐,另外不能与数据库连动.于是想找一款快速绘制er图,并且能够和数据库连动的软件工具. eclipse插 ...

  10. JQuery实战--可以编辑的表格

    廊坊下雪了,15年的第二场雪,比14的来的稍晚一些,停靠在11教门前的自行车,成了廊坊师范学院最美丽的风景线.还记得以前学习css的时候,就曾经接触过如何编写设计一些表格和表单的样式,例如如何设计表格 ...