【线程】 ThreadLocal

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013036274/article/details/54667586

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

简单说,ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,只要在本线程内,随时随地可以取,于是也就隔离了其他线程,主要解决多线程中数据数据因并发产生不一致问题,以此保证线程安全

先来看一下ThreadLocal类的方法。


  initialValue()方法:返回该线程局部变量的初始值,是一个延迟调用方法,在线程第1次调用get()set(Object)时才执行,并且仅执行1次。

    protected T initialValue() {
        return null;
    }

get()方法:返回当前线程所对应的线程局部变量。

/**
     * Returns the value in the currentthread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initializedto the value returned
     * by an invocation of the {@link#initialValue} method.
     *
     * @return the current thread's value ofthis thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e =map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

set()方法:设置当前线程的线程局部变量的值。

/**
     * Returns the value in the currentthread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initializedto the value returned
     * by an invocation of the {@link#initialValue} method.
     *
     * @return the current thread's value ofthis thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e =map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

remove()方法:将当前线程局部变量的值删除,目的是为了减少内存的占用

    /**
     * Removes the current thread's value forthis thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the currentthread, its value will be
     * reinitialized by invoking its {@link#initialValue} method,
     * unless its value is {@linkplain #setset} by the current thread
     * in the interim.  This may result in multiple invocations ofthe
     * <tt>initialValue</tt> methodin the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m =getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
【实践】

ThreadLocal测试:

package com.bjpowernode.drp.util;

/**
 * ThreadLocal测试
 * 
 * @author happy
 * 
 */
public class TestThreadLocal {
	// 通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
	private static ThreadLocal<Integer> threadNum = new ThreadLocal<Integer>() {
		public Integer initialValue() {
			return 0;
		}
	};

	// 获取下一个序列值
	public int getNextNum() {
		threadNum.set(threadNum.get() + 1);
		return threadNum.get();
	}

	private static class TestClient extends Thread {
		private TestThreadLocal testThreadLocal;

		public TestClient(TestThreadLocal testThreadLocal) {
			this.testThreadLocal = testThreadLocal;
		}

		public void run() {
			for (int i = 0; i < 3; i++) {
				System.out.println("线程[" + Thread.currentThread().getName()
						+ "] --> [" + testThreadLocal.getNextNum() + "]");
			}
		}

		public static void main(String[] args) {
			TestThreadLocal test = new TestThreadLocal();
			// 创建 3个线程,共享testThreadLocal,但各自产生序列号
			TestClient t1 = new TestClient(test);
			TestClient t2 = new TestClient(test);
			TestClient t3 = new TestClient(test);
			t1.start();
			t2.start();
			t3.start();
		}
	}
}
drp中的实例:

        不同的线程在使用ConnectionManager时,先判断connectionHolder.get()是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个connectionHolder.get()就可以做到在本线程内共享了。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 采用ThreadLocal封装Connection
 * 
 * @author Administrator
 * 
 */
public class ConnectionManager {

	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();

	/**
	 * 得到Connection
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		// 先从线程变量中获取connection
		Connection conn = connectionHolder.get();
		// 如果在当前线程中没有绑定相应的Connection
		if (conn == null) {
			try {
				JdbcConfig jdbcConfig = XmlConfigReader.getInstance()
						.getJdbcConfig();
				Class.forName(jdbcConfig.getDriverName());
				conn = DriverManager.getConnection(jdbcConfig.getUrl(),
						jdbcConfig.getUserName(), jdbcConfig.getPassword());
				// 将Connection设置到ThreadLocal
				connectionHolder.set(conn);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			} catch (SQLException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			}
		}
		return conn;
	}

	/**
	 * 关掉connection
	 */
	public static void closeConnection() {
		Connection conn = connectionHolder.get();
		if (conn != null) {
			try {
				conn.close();
				// 从ThreadLocal中清除Connection
				connectionHolder.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

【比较】

  ThreadLocal和Synchonized都用于解决多线程并发访问。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的是各自相应的对象,这样就隔离了多个线程对数据的数据共享。

     Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

猜你喜欢

转载自blog.csdn.net/u013036274/article/details/54667586