对ThreadLocal的初步了解

1.为什么使用ThreadLocal

    在说作用之前先看一段通过单例模式(懒汉:需要时才创建。相反的是饿汉模式,类一加载就创建)获取数据库连接池对象的代码

public class Test1 {
	private Test1() {};
	
	private static Connection conn;
	
	/**
	 * 获得数据库连接对象
	 */
	public static Connection getConnection() throws SQLException {
		if(conn == null) {  ---------语句1
			conn = DriverManager.getConnection("", "", "");			
		}
		
		return conn;
	}
	
	/**
	 * 关闭连接对象
	 */
	public static void closeConnection() throws SQLException {
		if(conn != null) {
			conn.close();
		}
	}
}

    在对数据库进行操作的时候,通过单例模式系统只保留一个实例减少资源的开销。获取数据库连接需要时间和额外的开销,使用单例模式,除了第一次创建外,其余都是使用现有的对象,减少创建的时间。但是使用以上代码在多线程环境下,会出现线程安全的问题。如:(1)当线程A运行到语句一的时候,if语句判断为true。而此时线程A分配的时间刚好执行完。此时线程B分配到时间,也刚好执行语句1,由于线程A判断为true后还未获取连接对象,时间片就执行完毕。所以此处线程B判断结果也是为true。这就会导致线程A和线程B都会获取数据库连接。单例模式也失去了意义。(2)前面一种假设可能不会导致程序运行的失败。但是当某个线程通过getConnection获取到数据库连接之后,另一个线程则执行closeConnection()方法关闭连接,就会造成程序的运行异常。

    针对上面的两种情况,可以将getConnection方法和closeConnection方法都加上syncronized修饰,并且在使用conn对象的逻辑代码中加上同步锁,就可以避免。但是这样又带来一个问题,就是同一时刻,只能又一个线程对conn对象进行操作,其他需要conn对象的线程将处于阻塞状态,这样会导致效率的低下。

    这时就可以使用ThreadLocal来解决多线程环境下,共享对象的线程安全问题,并减少解决线程安全而带来的效率低下问题。


2.什么是ThreadLocal

    当使用ThreadLocal维护变量时,会为每个使用该变量的线程维护一个该变量的副本,副本是独立的。但是此时会带来一个问题,就是每个线程都有一个该变量的副本,那相对而言使用ThreadLocal会比不用ThreadLocal额外占用内存。


3.ThreadLocal类中常用的方法

    (1)  set(T value)

扫描二维码关注公众号,回复: 2040100 查看本文章
public void set(T value) {
        Thread t = Thread.currentThread();  //获取当前线程
        ThreadLocalMap map = getMap(t);     //ThreadLocalMap是ThreaLocal中的一个内部类,getMap()方法为获取当前线程的threadLocals属                                                性,TheadLocalMap中维护一个key-value类型的数组。
        if (map != null)
            map.set(this, value);    //若map已经存在,以线程对象为key,存储的值为value存入map的key-value数组中。下标通过key的hash值计                                        算得到
        else
            createMap(t, value);     //若map不存在,创建ThreadLocalMap并存入
    }

    (2)V get(K k)

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);    //获取线程中的threadLocals属性
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);    //根据当前线程对象获得value
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();    //当线程中的threadLocals对象未初始化,调用该方法,该方法的第一行调用protected T initialValue()方                                        法用于子类重写(使用者重写)获得value的方法。而后初始化线程的threadLocals属性,创建ThreadLocal                                        Map对象。
    }

    (3)remove()

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

    移除ThreadLocalMap中的变量副本。

总结:每个Thread线程类中都有一个threadLocals属性,该属性是ThreadLocalMap类型,但该类型不是map的子类!!当线程通过ThreadLocal的set方法存储变量的副本时,是将该副本存储到Thread当前线程对象的theadLocals属性中,而每个线程对象都有各自的threadLocals属性,这保证了多线程下共享变量的线程安全问题。


4.ThreadLocal的实验

public class Test {
	ThreadLocal<String> local = new ThreadLocal<String>();
	
	public void set() {
		local.set(Thread.currentThread().getName());
	}
	
	public void get() {
		System.out.println(local.get());
	}
	
	public static void main(String[] args) throws InterruptedException {
		Test t = new Test();
		
		
		t.set();  //实质是将变量副本存储到main线程的threadlocals属性中
		t.get();
		
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				t.set(); //实质是将变量副本存储到thread线程的threadlocals属性中
				t.get();
			}
		});
		
		thread.start();
		thread.join();
		
		t.get();
		
		
	}
}

    输出结果为:

5.改写 1 中的单例子模式获取数据库连接对象的方法

private Test1() {};
	
	private static ThreadLocal<Connection> local = new ThreadLocal<>();
	public static Connection conn;
	
	/**
	 * 获得数据库连接对象
	 */
	public static Connection getConnection() throws SQLException {
		if(conn == null) {
			conn = DriverManager.getConnection("", "", "");			
		}
		
		if(null == local.get()) {
			local.set(conn);
		}
		
		return local.get();
	}
	
	/**
	 * 关闭连接对象
	 */
	public static void closeConnection() throws SQLException {
		if(local.get() != null) {
			local.get().close();
		}
	}







    

        

        



猜你喜欢

转载自blog.csdn.net/qiaoqiyu6416/article/details/80970309