ThreadLocal的实现原理,及使用实例,解决spring,hibernate非web项目下的懒加载 no session or session was closed(1)!

来源:互联网 发布:物理软件初中免费 编辑:程序博客网 时间:2024/05/18 14:24

JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的局部变量独立问题...

 

查看API我们可以查看ThreadLocal的定义与方法:

 

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 getset 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

例如,以下类生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

 

构造方法摘要ThreadLocal()
          创建一个线程本地变量。  方法摘要 Tget()
          返回此线程局部变量的当前线程副本中的值。protected  TinitialValue()
          返回此线程局部变量的当前线程的“初始值”。 voidremove()
          移除此线程局部变量当前线程的值。 voidset(T value)
          将此线程局部变量的当前线程副本中的值设置为指定值。

 

remove()

移除此线程局部变量当前线程的值,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

 

initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。如果泛型对象是Integer,可以定义内部内,重写initialValue() 返回0等Integer类型的数据!

 

ThreadLocal并非一个所谓的 "local thread (本地线程)",而是处理线程中的局部变量的.其可以理解为:"local thread variable(线程局部变量)"!

 

线程局部变量已经有很多语言使用了,并非是Java独创...很多语言(如IBM IBM XL FORTRAN)在语法层面就提供线程局部变量。

 

ThreadLocal的具体实现原理大致如下:

 

 

在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。其实现原理大致如下:

 

 

 

如何使用:

 

 

 

打印:

 

thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]

 

TestThread tt1 = new TestThread(sn);
TestThread tt2 = new TestThread(sn);
TestThread tt3 = new TestThread(sn);

输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber (sn) 实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本!

 

 

 

让我们再看个简单的实例:

package cn.vicky.chapt05;/** * * @author Vicky.H */public class ThreadLocalTest1 {    /** 实现了每个线程的静态变量不同 **/    private static ThreadLocal<String> val = new ThreadLocal<String>();    public static class Thread1 extends Thread {        private String name;        public Thread1(String name) {            this.name = name;        }        public void run() {            System.out.println(name + "初始值:" + val.get());            val.set("v[" + name + "]");            System.out.println(name + "设置后值:" + val.get());        }    }    public static class Thread2 extends Thread {        private String name;        public Thread2(String name) {            this.name = name;        }        public void run() {            System.out.println(name + "初始值:" + val.get());            val.set("v[" + name + "]");            System.out.println(name + "设置后值:" + val.get());        }    }    public static void main(String[] args) {        val.set("1");        System.out.println("主程序中设置值:" + val.get());        (new Thread1("A1")).start();        (new Thread1("A")).start();        (new Thread2("B1")).start();    }//主程序中设置值:1//A1初始值:null//A1设置后值:v[A1]//B1初始值:null//A初始值:null//A设置后值:v[A]//B1设置后值:v[B1]    //总结 //ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。//ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。 //ThreadLocal不能使用原子类型,只能使用Object类型。//ThreadLocal的使用比synchronized要简单得多。 //ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。 //Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。 //当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。 }


 

package cn.vicky.chapt05;/** * * @author Vicky.H */public class ThreadLocalTest2 {    /** 实现了每个线程的静态变量不同 **/    private static String val;    public static class Thread1 extends Thread {        private String name;        public Thread1(String name) {            this.name = name;        }        public void run() {            System.out.println(name + "初始值:" + val);            val = "v[" + name + "]";            System.out.println(name + "设置后值:" + val);        }    }    public static class Thread2 extends Thread {        private String name;        public Thread2(String name) {            this.name = name;        }        public void run() {            System.out.println(name + "初始值:" + val);            val = "v[" + name + "]";            System.out.println(name + "设置后值:" + val);        }    }    public static void main(String[] args) {        val = "1";        System.out.println("主程序中设置值:" + val);        (new Thread1("A1")).start();        (new Thread1("A")).start();        (new Thread2("B1")).start();    }    //主程序中设置值:1//A1初始值:1//A1设置后值:v[A1]//A初始值:v[A1]//A设置后值:v[A]//B1初始值:v[A]//B1设置后值:v[B1]}