首页 › 程序设计 › java

Spring ThreadLocal 基础知识

泡在网上的日子 / 文 发表于2013-06-22 00:05 次阅读 spring

Spring通过各种模板类降低了开发者使用各种数据持久技术的难度,这些模板类都是线程安全的。但是这些资源本身却是非线程安全的。根据传统的经验,如果某个对象是非线程安全的话,在多线程的环境下,对于对象的访问都必须采用同步机制,但是模板类并没有采用同步机制,因为线程的同步会降低并发性,Spring的模板类就是采用ThreadLocal 解决了在多线程环境下,不采用同步的方式解决了多线程的难题。

JDK1.2中提供了java.lang.ThreadLocal,他不是一个线程,而是线程的一个本地化对象。当工作于多线程环境中的对象使用ThreadLocad进行维护的时候,ThreadLocad会为每一个使用这个变量的线程分配一个独立的变量副本。所以每一个线程可以独立的改变自己的副本。而不会影响其他线程所对应的副本。从线程的角度来看,就如同使用局部变量一样。

ThreadLocal的接口很简单,只有4个方法:

void set(Object value)
public Object get()
public void remove()
protected object initialValue()

注意:从jdk5开始,支持泛型。

ThreadLocal为了为每一个线程维护一个独立的变量副本,实现思路是在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,key是线程对象,value是对应的变量副本。自己写一个比较幼稚的版本吧:

class MyThreadLocal {
private Map map = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
map.put(Thread.currentThread(), newValue);
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object obj = map.get(currentThread);
if (null == obj && !map.containsKey(currentThread)) {
obj = initialValue();
map.put(currentThread, obj);
}
return obj;
}
public void remove() {
map.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}

有兴趣的朋友可以看看jdk中的ThreadLocal是如何实现的。

下面我们用一个实例来结束这篇文章:

public class SequenceNumber {
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
        public Integer initialValue() {
            return 0;
        }
    };
    public int getNextNum() {
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }
    private static class TestClient extends Thread {
        private SequenceNumber sequenceNumber;
        public TestClient(SequenceNumber sequenceNumber) {
            this.sequenceNumber = sequenceNumber;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("thread[" + Thread.currentThread().getName() + "]sequenceNumber[" + sequenceNumber.getNextNum() + "]");
            }
        }
    }
    public static void main(String[] args) {
        SequenceNumber sequenceNumber = new SequenceNumber();
        new TestClient(sequenceNumber).start();
        new TestClient(sequenceNumber).start();
        new TestClient(sequenceNumber).start();
    }
}

运行结果:

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

可以看出虽然每个线程都共享一个sequenceNumber,但是却没有互相干扰,而是打印出自己独立的序列号。因此很好的证明了上面的结论:

ThreadLocal为了为每一个线程维护一个独立的变量副本,实现思路是在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,key是线程对象,value是对应的变量副本。


收藏 赞 (0) 踩 (0)
上一篇:如何写出高效率的正则表达式
如果纯粹是为了挑战自己的正则水平,用来实现一些特效(例如使用正则表达式计算质数、解线性方程),效率不是问题;如果所写的正则表达式只是为了满足一两次、几十次的运行,优化与否区别也不太大。但是,如果所写的正则表达式会百万次、千万次地运行,效率
下一篇:spring+mybatis加载属性文件设置数据源失败原因及解决方案
spring3 + mybatis 中,使用: !-- 属性文件配置 --context:property-placeholder location="classpath:config.properties"/ 加载属性文件,然后在spring-database中使用属性文件值时失败: !-- JNDI数据源 -- bean id="dataSource" class="org.springframew