从ThreadLocal 理解线程安全

看到很多框架中都使用了ThreadLocal ,单从名字来说很可能把他理解成为一个“本地线程”之类的玩意儿。。。

先上代码:

package com.tiger.Thread.concurrent;

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

public class testLocalThread {

    private ThreadLocal<Integer> i = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public void add(int num){
        i.set(i.get().intValue()+num);
    }

    public int getI(){
        return i.get();
    }

    /*private int i = 0;
    public synchronized void add(int num){
        i += num;
    }

    public synchronized int getI(){
        return i;
    }*/
    public static void main(String[] args) throws InterruptedException {

        final testLocalThread t = new testLocalThread();

        ExecutorService es = Executors.newFixedThreadPool(2);
        for(int i=0;i<2;i++){
            es.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    t.add(1);
                    System.out.println(t.getI());
                }
            });
        }

        es.shutdown();
        es.awaitTermination(2, TimeUnit.SECONDS);

    }

}

先从结果来分析:若用ThreadLocal,多个线程执行后,每个线程的输出都为1,

而直接使用int 变量则会看到计算结果是线程间相互作用的,而且如果不对普通变量进行同步控制还会出现意料之外的结果。

当我们说线程安全的时候其实是说某个状态在线程之间的一致性。更直接的说就是多个线程在同一时刻所看到的变量状态是一致的,但是在java的内存模型里我们可以看到变量对于线程是有可见性的,也就是说某一时刻当变量状态对其他线程不可见时,那么其他线程在不知情的情况下看到和使用的很有可能是一个过期的状态。

在Java Concurrency In Practice中对线程安全有几个建议:

1.无状态对象一定是线程安全的

2.不可变对象一定是线程安全的

3.用锁来保证互斥性和可见性

4.保障线程安全的2个整体思路:线程封闭和安全发布

回到我们的例子正是对这2个思路的体现:

ThreadLocal是见变量封闭在各自线程内部(具体实现参见源码)并未进行发布,对外没有状态,也自然就没有所谓的同步控制一说。

而一般变量当调用add()或者get()方法时实际上便将状态发布了出来,所有线程均可使用。当多个线程使用一个有状态并且可以改变的变量时,就必须考虑安全发布这个变量,需要用锁来保证变量的可见性(当然可以用atomic类型来进行安全发布避免使用锁带来的竞争)。

之前看到网上有不少文章都将ThreadLocal和数据在线程间的共享拉上了关系,个人认为可能会小小的对理解产生偏差。ThreadLocal只是让变量在各个线程间属性和行为一致,跟状态共享没有关系。

时间: 2024-11-05 21:58:09

从ThreadLocal 理解线程安全的相关文章

深入理解线程本地变量ThreadLocal

ThreadLocal理解: 如果在多线程并发环境中,一个可变对象涉及到共享与竞争,那么该可变对象就一定会涉及到线程间同步操作,这是多线程并发问题. 否则该可变对象将作为线程私有对象,可通过ThreadLocal进行管理,实现线程间私有对象隔离的目的. 可以发现,ThreadLocal并没有解决多线程并发的问题,因为ThreadLocal管理的可变对象的性质本来就不会涉及到多线程并发而引发的共享.竞争和同步问题,使用ThreadLocal管理只是方便了多线程获取和使用该私有可变对象的途径和方式.

Android线程管理之ThreadLocal理解及应用场景(五)

前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣!查阅了一下资料发现Android最重要的Handler消息机制里面的Looper存储也是采用ThreadLocal,开源框架EventBus存储当前线程下的发送事件队列状态也是采用ThreadLocal,那么为何要使用ThreadLocal呢?ThreadLocal是什么呢?它能解决什么样的问题呢

Spring学习11- Spring使用ThreadLocal解决线程安全问题

ThreadLocal是什么      早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”.其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些.      当使用ThreadLoca

ThreadLocal理解和运用

ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本. 从线程的角度看,目标变量就象是线程的本地变量,这也是类名中"Local"所要表达的意

聊聊JVM(五)从JVM角度理解线程

这篇说说如何从JVM的角度来理解线程,可以对Java的线程模型有一个更加深入的理解,对GC的一些细节也会理解地更加深刻.本文基于HotSpot的OpenJDK7实现. 我们知道JVM主要是用C++实现的,JVM定义的Thread的类继承结构如下: Class hierarchy - Thread - NamedThread - VMThread - ConcurrentGCThread - WorkerThread - GangWorker - GCTaskThread - JavaThread

当ThreadLocal碰上线程池

ThreadLocal使用 ThreadLocal可以让线程拥有本地变量,在web环境中,为了方便代码解耦,我们通常用它来保存上下文信息,然后用一个util类提供访问入口,从controller层到service层可以很方便的获取上下文.下面我们通过代码来研究一下ThreadLocal. 新建一个ThreadContext类,用于保存线程上下文信息 public class ThreadContext { private static ThreadLocal<UserObj> userReso

ThreadLocal,LinkedBlockingQueue,线程池 获取数据库连接

<pre name="code" class="java">package com.ctl.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import java.ut

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

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

ThreadLocal,LinkedBlockingQueue,线程池 获取数据库连接2改进

package com.ctl.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import com.ctl.util.ConfigU