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

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]
}


 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值