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

原版本见

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. 计算机网络之套接字SOCKET

    当某个应用进程启动系统调用时,控制权就从应用进程传递给了系统调用接口. 此接口再将控制权传递给计算机的操作系统.操作系统将此调用转给某个内部过程,并执行所请求的操作. 内部过程一旦执行完毕,控制权就又 ...

  2. Android桌面小插件——Widget

    Android桌面小插件--Widget 效果图 实现 1. 创建Widget类 创建一个Widget类,并实现页面创建的时候,就实现显示时间 package com.kongqw.kqwwidget ...

  3. J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用

    J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用   spring 中获得由spring所配置的hibernate的操作对象,然后利用此对象进行,保存,修 ...

  4. Python实现八大排序算法(转载)+ 桶排序(原创)

    插入排序 核心思想 代码实现 希尔排序 核心思想 代码实现 冒泡排序 核心思想 代码实现 快速排序 核心思想 代码实现 直接选择排序 核心思想 代码实现 堆排序 核心思想 代码实现 归并排序 核心思想 ...

  5. 浅谈机器人控制与仿真设计----RDS和ROS

    机器人控制.仿真或实验,主要由三个部分组成,机器人.环境和算法. 当然各部分又包含很多子部分和功能,这里主要以仿真为主,为了使得仿真结果能够直接应用到实际机器人上,这里分别以RDS和ROS对比介绍.h ...

  6. 一道有趣的Twitter技术面试题

    来自:http://blog.jobbole.com/50705/ 看下面这个图片” “在这个图片里我们有不同高度的墙.这个图片由一个整数数组所代表,数组中每个数是墙的高度.上边的图可以表示为数组[2 ...

  7. SublimeText3解决中文乱码

    1)安装Sublime Package Control.     在Sublime Text 3上用Ctrl+-打开控制台并在里面输入以下代码,Sublime Text 2就会自动安装Package ...

  8. JBOSS EAP6 系列二 客户端访问位于EAR中的EJB时,jndi name要遵守的规则

    EJB 的 jndi语法(在整个调用远程ejb的过程中语法的遵循是相当重要的) 参见jboss-as-quickstarts-7.1.1.CR2\ejb-remote\client\src\main\ ...

  9. CentOS下将php和mysql命令加入到环境变量中的几种方法

    Linux CentOS配置LAPM环境时,为了方便,将php和mysql命令加到系统环境命令,下面我们记录几种在linux下将php和mysql加入到环境变量中的方法. 如果在没有添加到环境变量之前 ...

  10. Hive-RCFile文件存储格式

    在新建Hive表时,可以使用stored as rcfile来指定hive文件的存储方式为RCFile. 一.RCFile文件结构 下图是一个RCFile的文件结构形式. 从上图可以看出: 1)一张表 ...