详解ThreadLocal模式

ThreadLocal:java.lang.ThreadLocal
	作用:解决多线程中数据共享的问题,即:解决了同一线程中隶属于不同开发层次(表示层、业务层、持久层)的数据共享问题,故可以对执行逻辑和执行数据进行有效的解耦
	应用:应用在同一个线程的不同开发层次中共享数据。例如:Struts2中的ActionContext、Spring中管理数据库的连接、Hibernate中的Session
	实现:
		1)建立一个类,并在其中封装一个静态的ThreadLocal变量,使其成为一个共享的数据环境。
		2)在类中实现访问静态ThreadLocal变量的静态方法(设值和取值)

	【说明】:ThreadLocal通过操作当前线程中的一个内部变量(ThreadLocalMap)来达到与其它线程隔离的目的。
		1)ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量
		2)线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或get操作时创建的,在创建之前会检查当前线程中的ThreadLocalMap是否为null,如果为null则创建,如果不为null,则使用已经存在的ThreadLocalMap
		3)使用当前线程的ThreadLocalMap的关键在于:使用当前的ThreadLocal的实例作为key进行储存
	【重要】:
		1)一个ThreadLocal只能储存一个变量,如果重复调用ThreadLocal的set方法,则新值会将旧值覆盖。
		2)如果需要在一个线程中共享多个变量,则可以将多个变量封装到一个对象中,然后将该对象存储在ThreadLocal中。Struts2中的ActionContext就是这样设计的。
		3)在ThreadLocalMap中,如果当前线程中定义了多个不同的ThreadLocal的实例,则它们会作为不同的key进行储存而不会相互干扰!


	java.lang.Thread的部分源码:
		public class Thread implements Runnable {
			//这里省略了许多其它的代码
			
			ThreadLocal.ThreadLocalMap threadLocals = null;
		}
		
	java.lang.ThreadLocal的部分源码:
		public Class ThreadLocal<T> {
			//这里省略了许多其它的代码
			
			// 将value的值保存在当前线程的本地变量表(ThreadLocalMap)中
			public void set(T value){
				// 获取当前线程
				Thread t = Thread.currentThread();
				// 调用getMap方法获得当前线程中的本地变量表ThreadLocalMap
				ThreadLocalMap map = getMap(t);
				// 如果本地变量表ThreadLocalMap已经存在,则直接使用
				if (map != null){
					// 以当前的ThreadLocal的实例作为key,储存在当前线程中。ThreadLocalMap
					// ThreadLocalMap中,如果当前线程中定义了多个不同的ThreadLocal的实例,则它们会作为不同的key进行储存而不会相互干扰
					// ThreadLocalMap的set方法:private void set(ThreadLocal<?> key, Object value)
					map.set(this, value);
				} else {
					// 如果ThreadLocalMap不存在,则初始化map
					createMap(t, value);
				}	
			}
			
			// 获取当前线程中以当前ThreadLocal实例为key的变量值
			public T get() {
				// 获取当前线程
				Thread t = Thread.currentThread();
				// 获取当前线程中的ThreadLocalMap
				ThreadLocalMap map = getMap(t);
				if (map != null) {
					// 获取当前线程中以当前ThreadLocal实例为key的变量值
					ThreadLocalMap.Entry e = map.getEntry(this);
					if (e != null) {
						return (T)e.value;
					}
				}
				
				// 当map不存在时,初始化map,并返回null
				return setInitialValue();
			}
			
			// 从当前线程中获取与之对应的ThreadLocalMap
			ThreadLocalMap getMap(Thread t) {
				return t.threadlocals;
			}
			
			// 创建当前线程中的ThreadLocalMap
			void createMap(Thread t, T firstValue) {
				// 调用构造函数生成当前线程中的ThreadLocalMap
				t.threadLocals = new ThreadLocalMap(this, firstValue);
			}
			
			// 初始化当前线程的ThreadLocalMap
			private T setInitialValue() {
				T value = initialValue();
				Thread t = Thread.currentThread();
				ThreadLocalMap map = getMap(t);
				if (map != null)
					map.set(this, value);
				else
					createMap(t, value);
				return value;
			}
			
			// 若当前线程的ThreadLocalMap不存在时,调用ThreadLocal的get()方法默认返回null。
			// 若子类覆盖父类ThreadLocal的该方法,则可以设置当(当前线程的)ThreadLocalMap不存在时,get()方法默认返回的值。
			protected T initialValue() {
				return null;
			}

			
			// ThreadLocalMap的定义 
			static class ThreadLocalMap{
				// ...
			}
			
		}


	应用举例:Struts2中的ActionContext
		
	ActionContext在内部封装了一个静态的ThreadLocal的实例,而这一实例操作的对象()又是ActionContext本身。
		
	Struts2中的ActionContext的部分源码:	
		public class ActionContext implements Serializable {
			// 此处省略了很多代码
			
			// 封装了一个ThreadLocal变量,储存的内容是ActionContext本身
			static ThreadLocal actionContext = new ThreadLocal();
			
			// 在ThreadLocal中设置ActionContext,绑定到当前线程。
			public static void setContext(ActionContext context){
				actionContext.set(context);
				// 注:ThreadLocal的set方法调用了ThreadLocalMap(当前线程的本地变量表)的set(threadLocal, value)方法:以当前的ThreadLocal的实例作为key,储存在当前线程的ThreadLocalMap属性中
			}
			
			// 返回当前线程中储存的ActionContext
			public static ActionContext getContext(){
				// 返回当前线程中储存的ActionContext
				return (ActionContext) actionContext.get();
			}
			
		}



	

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章