欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ThreadLocal 原理

程序员文章站 2022-06-01 17:53:28
...

由于Alibaba面试官提到ThreadLoacl,但是由于之前根本没有使用过,也没真正的去专研这个类。连夜赶出一篇文章。

ThreadLocal

ThreadLocal是解决多线程访问同一变量,保证线程安全产生的。它为每一个使用变量的线程提供一个独立的副本。换句话说,每个线程都持有一个该对象的副本。自己对自己的副本进行操作就不会出现线程不安全的问题。它将每个线程和对象的副本存放在ThreadLocalMap(ThreadLocal的一个静态内部类)中,ThreadLocalMap下面是一个Entry[]数组。

ThreadLocal 原理

ThreadLocal 原理

ThreadLocal 原理

在创建ThreadLocal时,ThreadLocalMap内是空的,当调用get()/set()方法时,就会对其中的ThreadLocalMap获取/添加操作。

ThreadLocal 原理

例子:

package com.example.demo.juc;

/**
 * ThreadLocalTest
 * @author Logan
 */
public class ThreadLocalTest {

	// long Thread Local
	private ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
	// string Thread Local
	private ThreadLocal<String> stringLocal = new ThreadLocal<String>();

	public static void main(String[] args) throws InterruptedException {
		final ThreadLocalTest threadLocalTest = new ThreadLocalTest();
		// 设置值
		threadLocalTest.set();
		System.out.println("===1===");
		System.out.println(threadLocalTest.getLongLocal().get());
		System.out.println(threadLocalTest.getStringLocal().get());

		// 创建线程
		Thread thread = new Thread(()->{
			// 当这里调用了set方法,进一步调用了ThreadLocal的set方法是,
			// 会将ThreadLocal变量存储到该线程的ThreadLocalMap类型的成员变量threadLocals中,
			// 注意的是这个threadLocals变量是Thread线程的一个变量,而不是ThreadLocal类的变量。
			threadLocalTest.set();
			System.out.println("===2===");
			System.out.println(threadLocalTest.getLongLocal().get());
			System.out.println(threadLocalTest.getStringLocal().get());

		});
		thread.start();
		thread.join();// 强行加入
		System.out.println("===3===");
		System.out.println(threadLocalTest.getLongLocal().get());
		System.out.println(threadLocalTest.getStringLocal().get());

	}

	/**
	 *  设置线程的Id 和 Name
	 */
	private void set() {
		longLocal.set(Thread.currentThread().getId());
		stringLocal.set(Thread.currentThread().getName());
	}

	public ThreadLocal<Long> getLongLocal() {
		return longLocal;
	}

	public ThreadLocal<String> getStringLocal() {
		return stringLocal;
	}
}

 

ThreadLocal 的应用场景

1.获取数据库连接。


class DBTest {
	private final static String DB_URL = "url";
	private static ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>(){
		@Override
		protected Connection initialValue() {
			try {
				Connection connection = DriverManager.getConnection(DB_URL);
				connectionThreadLocal.set(connection);
				return connection;
			} catch (SQLException e) {
				e.printStackTrace();
				return null;
			}
		}
	};

	private static Connection getConnect(){
		return connectionThreadLocal.get();
	}
}

2.Session的管理

class SessionTest{

	private static ThreadLocal sessionThreadLocal = new ThreadLocal();
	public static Session getSession() {
		Session session = (Session) sessionThreadLocal.get();
		if(session == null){
			session = getSessionFactory().openSession();
		}
		return session;
	}
}

All in All : ThreadLocal是为了保证线程安全的,但是需要复制大量的副本,这也不是一个好事,增大的内存的开销。

线程安全还可以使用ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArrayList等容器,也可以保证线程安全。