`
该用户名已经存在
  • 浏览: 306443 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

另一个角度理解java的ThreadLocal

    博客分类:
  • Java
阅读更多
关于Java的ThreadLocal网上有大量的文章在谈这个问题,为什么这个东西大家说来说去乐此不疲呢,大约是因为它本身的神秘性,经常出现在一些框架中,但是自己又很少用。亦或是因为大家本身对它的理解各有偏颇,所以成了大家热议的话题。
我对ThreadLocal的理解也不深,这篇文章也不会去做什么深层次的研究,对源码分析的文章也有很多写的非常好的,我只是想从另外一个角度去认识ThreadLocal。

ThreadLocal被翻译成“线程本地变量”,从名字上讲首先它是“变量”,那么,我们就来看看Java中的各种变量。
一、局部变量
定义:方法中,代码块中,方法参数中定义的变量。
作用域:方法内部,代码块内部。其他方法,其他代码块不能访问。
生命周期:方法或者代码块调用开始到方法或者代码块调用结束。
共享性:方法内部,代码块内部共享,对于多个线程来讲,变量初始化到自己的工作内存中,主内存不存在该变量,所以线程之间不共享。
例子:
public void method1() {
        //方法执行到此处时变量a才被创建。
        int a=1;
        for (int i=0; i<10; i++) {
            System.out.println(i);
        }
        if (true) {
            int i= 2;
        }
        
        {
            int i=3;
        }
    }
    
public void method2() {
    int a=2;
}

同一个类中的method1和method2中都可以定义一个名为a的变量,同样method1中的for代码块,if代码块和{}代码块中都可以定义名为i的变量。它们的作用域都在方法内部或者代码块内部。

二、成员变量
定义:成员变量又称为成员属性,它是描述对象状态的数据,是类中很重要的组成成分。
作用域:整个类实例内部。
生命周期:伴随整个类实例始终,变量在创建类实例时被创建。
共享性(同一个类实例):在整个类实例共享,对于多线程来讲,变量被初始化到主内存中,每个线程拷贝变量到工作内存中进行操作,线程之间共享一个主内存变量,存在线程安全问题。
例子:
public class Clazz {
    private int a;
    
    public void method() {
        a ++;
    }
}

对于同一个Clazz类的实例来讲,变量a的作用域存在于整个Clazz实例,如果多线程之间共享一个Clazz实例,变量a是存在线程安全问题的。

三、全局变量
定义:全局变量在Java中也可以叫静态变量,通过static关键字修饰。
作用域:整个类。
生命周期:伴随整个类始终,变量在第一次使用该类时被创建。
共享性:在整个类共享,对于多线程来讲,变量被初始化到主内存中,每个线程拷贝变量到工作内存中进行操作,线程之间共享一个主内存变量,存在线程安全问题。
public class Clazz {
    public static int a = 0;
}

说了这么多,那么跟ThreadLocal有什么关系呢?
思考为什么要定义这么多的变量类型呢?
局部变量解决了方法内部,代码块内部行之间的变量传递问题。如果没有局部变量,不知道行之间怎么传递变量。
成员变量解决了类实例各方法之间的变量传递。如果没有成员变量,方法之间变量传递只能靠参数。
全局变量解决了类之间的变量传递。如果没有全局变量,类之间变量只能靠构造实例的时候相互传递。

那么ThreadLocal也是变量,该变量解决了什么问题呢?
ThreadLocal解决了变量在同一个线程内部之间的传递。
ThreadLocal首先不是解决线程安全问题,最显而易见的原因是ThreadLocal内部保存的变量在多线程之间不共享。数据都不共享,谈何线程安全?当然了,如果ThreadLocal内部保存的变量本身就是一个多线程共享的数据,那么还是会有线程安全的问题的。如果没有ThreadLocal,我们需要在同一个线程之内共享的数据大约只能通过方法传递了。这样可能会让代码显得杂乱。

四、ThreadLocal变量
定义:线程本地变量。
作用域:线程内部。
生命周期:伴随线程执行始终,线程结束,变量生命结束。
共享性:多个线程之间不共享。






10
8
分享到:
评论
9 楼 liangcoder 2014-03-11  
博主总结的相当到位!从作用域 生命周期 共享性的纬度比较了Java中常见的变量类型,这是一个种很不错的方式,同理可适用于volatile类型的变量。

看了下评论,关于ThreadLocal变量对于线程安全的影响,同意楼主的观点 ”ThreadLocal的变量不存在线程安全的问题“,额外补充一点: ThreadLocal变量可以解决线程不安全的问题。

举例说明:
对于线程不安全的SimpleDataFormat对象,如果在线程池中使用并声明为ThreadLocal类型,可以有效的减少其对象数量,同事保持线程安全。
8 楼 该用户名已经存在 2014-03-11  
kmkim 写道
“如果ThreadLocal内部保存的变量本身就是一个多线程共享的数据,那么还是会有线程安全的问题的。”


请问LZ说的这句话怎么理解?(我理解的ThreadLocal是每个线程自己保存一个副本到自己的工作内存,然后不同步到主内存。这样ThreadLocal内部保存的变量本身就不会是一个多线程共享的数据了,不知道我理解的是不是有误,麻烦指正。)


对。我的表述有误。
“如果ThreadLocal内部保存的变量本身就是一个多线程共享的数据,那么还是会有线程安全的问题的。”这句话说的不恰当。
正如你所说,无论ThreadLocal里面存放的是怎样的数据,多线程环境下你从当前线程的ThreadLocalMap中取出来的值确实是线程安全的,因为其他线程永远无法修改。
但是对于这个共享对象本身来讲,它的值是会被修改的。也不会多线程安全。
举个例子吧。

public class JavaVariable {
    //多线程共享值
    public static int a = 0;
    
    private static void plus1() throws Exception {
		//主线程将共享值修改成 10
		a = 10;
		for (int i = 0; i < 10; i++) {
			new Thread() {
				public void run() {
					//每个线程都将初始值0赋值给共享值a=0
					a = threadLocal.get();
					//修改a=1
					a++;
					//将修改后的a放到自己的ThreadLocalMap中,此后,对于该线程来说,获取到的ThreadLocalMap中的值永远是放进去的a=1,不会被其他线程修改。
					threadLocal.set(a);
					System.out.println("plus1:" + Thread.currentThread().getName() + ": " + threadLocal.get());
				}
			}.start();
		}
		Thread.sleep(1000);
		//但是主线程的a被其他线程修改了。输出不是10,而是1
		System.out.println(a);
	}
}

不知道这个例子恰到不恰当。其实这个大家一直难理解的就是ThreadLocal保存的值对于当前的线程来讲是线程安全的。我想说的是这个线程安全没什么关系。。因为一个值一旦被保存到ThreadLocal中时,他就不再和其他线程共享了,数据都不共享,怎么扯得上线程安不安全呢?

上面的例子就是每个线程修改共享的值。一般正常人用ThreadLocal都是这样的:
....
Object obj = threadLocal.get();
if (null == obj) {
    // 每个线程创建一个自己的Object,不和任何线程共享。
    obj = new Object();
    threadLocal.set(obj);
}

说ThreadLocal是线程安全的当然没错,但是说这样的话就好比说:你是看不见鬼的,废话,因为压根就没有鬼。

ThreadLocal不是为了解决线程安全的问题而设计的,是为了解决同一个线程内变量共享而设计的。
7 楼 该用户名已经存在 2014-03-11  
hunnuxiaobo 写道
lianglaiyang 写道
总结的非常好!
但我还想过一个问题,一直没有找个合适的答案,望不吝赐教:
我们tomcat是用线程池的,一个线程会被重复利用,那threadlocal会不会出现混乱的情况呢?

线程被重复利用,ThreadLocal也会重复利用,所以应该设置一个Filter,在请求结束后清理ThreadLocal,据我所知,Struts2里面就是这么做的。


对,因为ThreadLocalMap是Thread的一个引用存在的,只要Thread是一个,ThreadLocalMap就是一个,会重复利用。
一般情况下,我们自己用的话ThreadLocal里面都存放的是无状态的对象,只是便于在同一个线程中参数传递,所以个人认为即使重复利用也没有关系。
但是如果ThreadLocal里面存放的是有状态的对象的话,比如 Struts2里面的ActionContext。
public class ActionContext implements Serializable {
    static ThreadLocal actionContext = new ThreadLocal();
    ....
    public static void setContext(ActionContext context) {
        actionContext.set(context);
    }
}

在struts2的org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter类中,就会无论如何在请求结束后清除当前线程的ThreadLocalMap中的值:
} finally {
            prepare.cleanupRequest(request);
        }

public void cleanupRequest(HttpServletRequest request) {
    ActionContext.setContext(null);
}

6 楼 kmkim 2014-03-10  
“如果ThreadLocal内部保存的变量本身就是一个多线程共享的数据,那么还是会有线程安全的问题的。”


请问LZ说的这句话怎么理解?(我理解的ThreadLocal是每个线程自己保存一个副本到自己的工作内存,然后不同步到主内存。这样ThreadLocal内部保存的变量本身就不会是一个多线程共享的数据了,不知道我理解的是不是有误,麻烦指正。)
5 楼 timer_yin 2014-03-10  
感谢分享!!
4 楼 hunnuxiaobo 2014-03-10  
lianglaiyang 写道
总结的非常好!
但我还想过一个问题,一直没有找个合适的答案,望不吝赐教:
我们tomcat是用线程池的,一个线程会被重复利用,那threadlocal会不会出现混乱的情况呢?

线程被重复利用,ThreadLocal也会重复利用,所以应该设置一个Filter,在请求结束后清理ThreadLocal,据我所知,Struts2里面就是这么做的。
3 楼 lianglaiyang 2014-03-10  
总结的非常好!
但我还想过一个问题,一直没有找个合适的答案,望不吝赐教:
我们tomcat是用线程池的,一个线程会被重复利用,那threadlocal会不会出现混乱的情况呢?
2 楼 793059909 2014-03-09  
1 楼 xugangqiang 2014-03-09  
楼主写的非常好
这解决了我的一个困惑
思路非常好

相关推荐

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    java 简单的ThreadLocal示例

    java 简单的ThreadLocal示例

    理解ThreadLocal

    理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal

    java中ThreadLocal详解

    详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露

    JAVA ThreadLocal类深入

    深入研究java.lang.ThreadLocal类。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是 threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。

    java事务 - threadlocal

    ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量

    Java中ThreadLocal的设计与使用

    Java中ThreadLocal的设计与使用.doc

    彻底理解Java 中的ThreadLocal

    主要介绍了彻底理解Java 中的ThreadLocal的相关资料,需要的朋友可以参考下

    使用Java ThreadLocal.docx

    我们可以看到,通过这段代码实例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用...

    正确理解ThreadLocal.pdf

    正确理解ThreadLocal.pdf

    Java ThreadLocal类应用实战案例分析

    主要介绍了Java ThreadLocal类应用,结合具体案例形式分析了java ThreadLocal类的功能、原理、用法及相关操作注意事项,需要的朋友可以参考下

    ThreadLocal应用示例及理解

    ThreadLocal应用示例及理解,这个写了相关的示例,可以参考一下。

    java ThreadLocal使用案例详解

    主要为大家详细介绍了java ThreadLocal的使用案例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    Java资料-详解ThreadLocal

    Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal Java资料—详解ThreadLocal

    java中ThreadLocal类的使用

    NULL 博文链接:https://justsee.iteye.com/blog/791919

    深入理解 Java 之 ThreadLocal 工作原理1

    声明仅作学习。如有不适,请告知。清晰的看到一个线程Thread中存在一个ThreadLocalMap,ThreadLocalMap中的key对应ThreadLo

    ThreadLocal

    利用过滤器去获取request和response,进行将其设置到ThreadLocal从而保证我的请求的安装线,这样就能在java普通类中获取request和response

    彻底理解Java中的ThreadLocal

    ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。使用这个工具类可以很简洁地编写出优美的多线程程序。接下来通过本文给大家介绍Java中的ThreadLocal,需要的朋友可以参考下

    理解的ThreadLocal类的相关源码(用于博文引用源码下载)

    Java中ThreadLocal工具类(解决多线程程序中并发问题的一种新思路,主要为参数的拷贝问题),感兴趣的话可以查看博文,博文地址:http://blog.csdn.net/otengyue/article/details/38459327

Global site tag (gtag.js) - Google Analytics