欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Java多线程:深入ThreadLocal,多线程threadlocal,ThreadLocal与

来源: javaer 分享于  点击 29787 次 点评:288

Java多线程:深入ThreadLocal,多线程threadlocal,ThreadLocal与


ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。

准确的说,应该是ThreadLocal类型的变量内部的注册表(Map<Thread,T>)发生了变化,但ThreadLocal类型的变量本身的确是一个,这才是本质!

下面就做个例子:

一、ThreadLocal标准例子

定义了MyThreadLocal类,创建它的一个对象tlt,分别给四个线程使用,结果四个线程tlt变量并没有出现共用现象,二是各用各的,这说明,四个线程使用的是tlt的副本(克隆品)。

/** * 使用了ThreadLocal的类 * * @author leizhimin 2010-1-5 10:35:27 */ public class MyThreadLocal {         //定义了一个ThreadLocal变量,用来保存int或Integer数据         private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {                 @Override                 protected Integer initialValue() {                         return 0;                 }         };         public Integer getNextNum() {                 //将tl的值获取后加1,并更新设置t1的值                 tl.set(tl.get() + 1);                 return tl.get();         } }/** * 测试线程 * * @author leizhimin 2010-1-5 10:39:18 */ public class TestThread extends Thread {         private MyThreadLocal tlt = new MyThreadLocal();         public TestThread(MyThreadLocal tlt) {                 this.tlt = tlt;         }         @Override         public void run() {                 for (int i = 0; i < 3; i++) {                         System.out.println(Thread.currentThread().getName() + "\t" + tlt.getNextNum());                 }         } }/** * ThreadLocal测试 * * @author leizhimin 2010-1-5 10:43:48 */ public class Test {         public static void main(String[] args) {                 MyThreadLocal tlt = new MyThreadLocal();                 Thread t1 = new TestThread(tlt);                 Thread t2 = new TestThread(tlt);                 Thread t3 = new TestThread(tlt);                 Thread t4 = new TestThread(tlt);                 t1.start();                 t2.start();                 t3.start();                 t4.start();         } }

可以看出,三个线程各自独立编号,互不影响:

Thread-0  1 Thread-1  1 Thread-0  2 Thread-1  2 Thread-0  3 Thread-1  3 Thread-2  1 Thread-3  1 Thread-2  2 Thread-3  2 Thread-2  3 Thread-3  3 Process finished with exit code 0

tlt对象是一个,废话tl对象也是一个,因为组合关系是一对一的。但是tl对象内部的Map随着线程的增多,会创建很多Integer对象。只是Integer和int已经通用了。所以感觉不到Integer的对象属性。

二、不用ThreadLocal

假如不用ThreadLocal,只需要将MyThreadLocal类重新定义为:

/** * 使用了ThreadLocal的类 * * @author leizhimin 2010-1-5 10:35:27 */ public class MyThreadLocal {         private Integer t1 = 0;         public Integer getNextNum(){                 return t1=t1+1;         } //        //定义了一个ThreadLocal变量,用来保存int或Integer数据 //        private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() { //                @Override //                protected Integer initialValue() { //                        return 0; //                } //        }; // //        public Integer getNextNum() { //                //将tl的值获取后加1,并更新设置t1的值 //                tl.set(tl.get() + 1); //                return tl.get(); //        } }然后运行测试:Thread-2  1 Thread-2  2 Thread-1  4 Thread-1  6 Thread-3  3 Thread-3  9 Thread-3  10 Thread-1  8 Thread-0  7 Thread-0  11 Thread-0  12 Thread-2  5 Process finished with exit code 0

从这里可以看出,四个线程共享了tlt变量,结果每个线程都直接修改tlt的属性。

三、自己实现个ThreadLocal

package cn.outofmemory.test2; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 使用了ThreadLocal的类 * * @author leizhimin 2010-1-5 10:35:27 */ public class MyThreadLocal {         //定义了一个ThreadLocal变量,用来保存int或Integer数据         private cn.outofmemory.test2.ThreadLocal<Integer> tl = new cn.outofmemory.test2.ThreadLocal<Integer>() {                 @Override                 protected Integer initialValue() {                         return 0;                 }         };         public Integer getNextNum() {                 //将tl的值获取后加1,并更新设置t1的值                 tl.set(tl.get() + 1);                 return tl.get();         } } class ThreadLocal<T> {         private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());         public ThreadLocal() {         }         protected T initialValue() {                 return null;         }         public T get() {                 Thread t = Thread.currentThread();                 T obj = map.get(t);                 if (obj == null && !map.containsKey(t)) {                         obj = initialValue();                         map.put(t, obj);                 }                 return obj;         }         public void set(T value) {                 map.put(Thread.currentThread(), value);         }         public void remove() {                 map.remove(Thread.currentThread());         } }

运行测试:

Thread-0  1 Thread-0  2 Thread-0  3 Thread-2  1 Thread-2  2 Thread-3  1 Thread-2  3 Thread-3  2 Thread-1  1 Thread-3  3 Thread-1  2 Thread-1  3 Process finished with exit code 0

很意外,这个山寨版的ThreadLocal也同样运行很好,实现了JavaAPI中ThreadLocal的功能。

四、透过现象看本质

其实从程序角度看,tlt变量的确是一个,毫无疑问的。但是为什么打印出来的数字就互不影响呢?

是因为使用了Integer吗?-----不是。

原因是:protected T initialValue()和get(),因为每个线程在调用get()时候,发现Map中不存在就创建。调用它的时候,就创建了一个新变量,类型为T。每次都新建,当然各用个的互不影响了。

为了看清本质,将Integer换掉,重写部分类:

package cn.outofmemory.test2; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 使用了ThreadLocal的类 * * @author leizhimin 2010-1-5 10:35:27 */ public class MyThreadLocal {         //定义了一个ThreadLocal变量,用来保存int或Integer数据         //        private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() {         private cn.outofmemory.test2.ThreadLocal<Bean> tl = new cn.outofmemory.test2.ThreadLocal<Bean>() {                @Override                 protected Bean initialValue() {                         return new Bean();                 }         };         @Override         public String toString() {                 return "MyThreadLocal{" +                                 "tl=" + tl +                                 '}';         }         public Bean getBean() {                 return tl.get();         } } 
class ThreadLocal<T> {         private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());         public ThreadLocal() {         }         protected T initialValue() {                 return null;         }         public T get() {                 Thread t = Thread.currentThread();                 T obj = map.get(t);                 if (obj == null && !map.containsKey(t)) {                         obj = initialValue();                         map.put(t, obj);                 }                 return obj;         }         public void set(T value) {                 map.put(Thread.currentThread(), value);         }         public void remove() {                 map.remove(Thread.currentThread());         } }
package cn.outofmemory.test2; /** * 测试Bean * * @author leizhimin 2010-1-5 14:18:26 */ public class Bean {         private String id = "0";         private String name = "none";         public Bean() {         }         public Bean(String id, String name) {                 this.id = id;                 this.name = name;         }         public String getId() {                 return id;         }         public void setId(String id) {                 this.id = id;         }         public String getName() {                 return name;         }         public void setName(String name) {                 this.name = name;         }         public String showinfo() {                 return "Bean{" +                                 "id='" + id + '\'' +                                 ", name='" + name + '\'' +                                 '}';         } }
package cn.outofmemory.test2; /** * 测试线程 * * @author leizhimin 2010-1-5 10:39:18 */ public class TestThread extends Thread {         private MyThreadLocal tlt = new MyThreadLocal();         public TestThread(MyThreadLocal tlt) {                 this.tlt = tlt;         }         @Override         public void run() {                 System.out.println(">>>>>:" + tlt);                 for (int i = 0; i < 3; i++) {                         System.out.println(Thread.currentThread().getName() + "\t" +tlt.getBean()+"\t"+tlt.getBean().showinfo());                 }         } }

然后运行测试:

>>>>>:MyThreadLocal{tl=cn.outofmemory.test2.MyThreadLocal$1@1de3f2d} >>>>>:MyThreadLocal{tl=cn.outofmemory.test2.MyThreadLocal$1@1de3f2d} >>>>>:MyThreadLocal{tl=cn.outofmemory.test2.MyThreadLocal$1@1de3f2d} >>>>>:MyThreadLocal{tl=cn.outofmemory.test2.MyThreadLocal$1@1de3f2d} Thread-1  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Thread-2  cn.outofmemory.test2.Bean@fe64b9  Bean{id='0', name='none'} Thread-3  cn.outofmemory.test2.Bean@186db54  Bean{id='0', name='none'} Thread-2  cn.outofmemory.test2.Bean@fe64b9  Bean{id='0', name='none'} Thread-2  cn.outofmemory.test2.Bean@fe64b9  Bean{id='0', name='none'} Thread-0  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Thread-3  cn.outofmemory.test2.Bean@186db54  Bean{id='0', name='none'} Thread-3  cn.outofmemory.test2.Bean@186db54  Bean{id='0', name='none'} Thread-1  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Thread-0  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Thread-0  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Thread-1  cn.outofmemory.test2.Bean@291aff  Bean{id='0', name='none'} Process finished with exit code 0

从打印结果很清楚的看到,MyThreadLocal的tlt对象的确是一个,tlt对象里的ThreadLocal的tl对象也是一个,但是,将t1t给每个线程用的时候,线程会重新创建Bean对象加入到ThreadLocal的Map中去使用。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/258459

相关栏目:

用户点评