ThreadLocal 的整理

ThreadLocal

1.是什么。

ThreadLocal是一个线程局部变量的类

ThrealLocal 在解决非同步锁synchronized状态下的线程安全有重要意义的。(性能)

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。

我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。

虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。

按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但Spring的DAO模板类并未采用线程同步机制,因为线程同步限制了并发访问,会带来很大的性能损失。

此外,通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现难度。那模板类究竟仰丈何种魔法神功,可以在无需同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!

ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。

其他例子:

ThreadLocal  ~ Map  用来保存线程局部变量


2. 做什么,

为解决线程同步问题提供了一种新的思路

3.怎么用:

需要注意到一点是:ThreadLocal保存的临界资源类型。像Connection这种。需要的资源,线程完了,不用了,就释放掉。不是一个修改型的临界资源。

例如,银行取钱,

用到了临界资源Session


就是这个例子很好理解。两个线程不能同时使用一个Connection,此时,解决的办法有两个,

1.使用synchronized,同步锁,把connection锁住,一个线程使用完后,其他的线程才能使用。

2.使用ThreadLocal,每个线程拥有与此connection“想等的局部对象”是通过new出来的。(即,如果线程局部变量没有这个对象,就new一个出来)



public class ThreadLocal<T>
   
   
    
    extends 
    
    Object
   
   
 

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

例如,以下类生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

import java.util.concurrent.atomic.AtomicInteger;
public class UniqueThreadIdGenerator {
        // 这个是要保存的局部变量,像Connection
	private static final AtomicInteger uniqueId = new AtomicInteger(0);
	private static final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
		@Override
		protected Integer initialValue() {
			return uniqueId.getAndIncrement();
		}
	};
	public static int getCurrentThreadId() {
		return uniqueId.get();
	}
}




其中要注意重写那一段的代码,以前很少这样,在new一个实例对象的时候修改了ThreadLocal(或者其他类的方法)里面的方法。那个

在ThreadLocal中。initialValue()方法是被定义为 protected 的,明显是为了让子类重写的。

   protected T initialValue() {
        return null;
    }

下面这个是Hibernate中一个典型的ThreadLocal应用。

private static final ThreadLocal threadSession = new ThreadLocal();  
  
public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();  
    try {  
        if (s == null) {  
            s = getSessionFactory().openSession();  
            threadSession.set(s);  
        }  
    } catch (HibernateException ex) {  
        throw new InfrastructureException(ex);  
    }  
    return s;  
}  


3.怎么用

我现在已经知道了ThreadLocal是用来解决什么问题的。(线程同步的另一种思路)通过new 新的临界资源副本,保存到相应线程中。供该线程使用。

当我了解以上需求时,我考虑该在什么地方。。怎么样合理地来使用ThreadLocal(可以理解为一个map),


1.临界资源,同步资源等问题,发生在两种地方,

a.同线程的不同模块(对同一资源的操作)

b.不同线程的同一模块(........)

c.不同线程的不同模块(........)

先前,我们解决这种问题的首先想到的就是同步锁,对操作临界资源的代码块加锁(嵌套锁)。但是这很影响效率,


ThreadLocal就是另一种思路的产物,及线程局部变量(实际是new一个)。注意上面的a,b,c三种情况下,我们就需要临界资源的副本。以免不同线程/模块,操作统一临界资源,发生错误。

即在

a.同线程的不同模块,我们需要线程局部变量

b.不同线程的同一模块,我们需要......

c.不同线程的不同模块,我们需要......

归纳来看,我们需要临界资源的地方,都要取副本,不能直接使用本体



import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class ThreadLocalTest {
	// private static ThreadLocal<Integer> dataMap = new ThreadLocal<Integer>();
	private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();

	public static void main(String[] args) {

		for (int i = 0; i < 2; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()
							+ " has put data :" + data);
					// dataMap.set(data);
					// 这里是一个基本类型变量,
					// 可以放一个对象。包含很多数据的对象,或者集合
					/*
					 * MyThreadScopeData myData = new MyThreadScopeData();
					 * myData.setName("name" + data); myData.setAge(data);
					 * map.set(data);
					 */
					MyThreadScopeData.getThreadInstance()
							.setName("name" + data);// 本线程相关的对象,
					MyThreadScopeData.getThreadInstance().setAge(data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}

	static class A {
		public void get() {
			// int data = dataMap.get();
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("A from" + Thread.currentThread().getName()
					+ " get Mydata :" + myData.getName() + "  "
					+ myData.getAge());

		}
	}

	static class B {
		public void get() {
			// int data = dataMap.get();
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("B from" + Thread.currentThread().getName()
					+ " get Mydata :" + myData.getName() + "  "
					+ myData.getAge());

		}
	}
}

/**
 * 这是一个封装好了要用的线程局部变量的类。
 * 使用到了ThreadLocal,当在线程中调用这个类的getInstance()方法,能返回临界资源的“副本”,所以这个类的名字应该明确
 * ,返回什么副本对象,名字就应该是什么。 例如,是Connection,则我把它命名为ThreadScopeConnection.getInstance()
 * 就返回一个线程范围内的Connection
 * 
 * @author jake
 * 
 */
class ThreadScopeConnection {
	private static Connection conn = null;
	private static ThreadLocal<Connection> map = new ThreadLocal<Connection>();

	private ThreadScopeConnection() {

	}

	public static Connection getThreadScopeInstance() {
		Connection instance = map.get();
		// 这里只是为了让getConnection不报错
		String url = null;
		String user = null;
		String password = null;
		if (instance == null) {
			try {
				instance = DriverManager.getConnection(url, user, password);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			map.set(instance);
		}
		return instance;
	}

}

/**
 * 这个例子就不够明确,返回的是一个data?什么数据,不知道。
 * 当然它是正确的,但是它不好,它把两个对象封装到一个了,原本应该是--一个数据对象,和一个线程范围局部变量对象 *
 * 
 * @author jake
 * 
 */
class MyThreadScopeData {
	// 临界资源的副本.这里资源不一定是MyThreadScopeData这个类,也可以是Session,Connection这种对象,更好理解。
	// private static MyThreadScopeData instance = new MyThreadScopeData();
	private static MyThreadScopeData instance = null;
	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();

	private MyThreadScopeData() {

	}

	// 重构快捷键 Shift + Alt + R
	/**
	 * 以下是ThreadLocal的get()方法 public T get() { Thread t =
	 * Thread.currentThread(); //以当前线程为key取存放在map中的数据对象。 ThreadLocalMap map =
	 * getMap(t); if (map != null) { ThreadLocalMap.Entry e =
	 * map.getEntry(this); if (e != null) return (T)e.value; } return
	 * setInitialValue(); }
	 */
	public static MyThreadScopeData getThreadInstance() {
		MyThreadScopeData instance = map.get();
		if (instance == null) {
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}

	

	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}


以上是摘出自黑马训练营的一段代码,有点不好的地方,而且老师也没有很明白地讲明。自己小结了下。




另外:Iteye的这篇博文还是对我很有帮助的。

http://www.iteye.com/topic/103804


多追究下去不是我本意,本篇学习日记就到这,以后再实际项目中希望能慢慢深入






































  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
public static final ThreadLocal listOfSuperPermissionObjectTreeResponse = new ThreadLocal; 这是一个具有公共、静态和最终修饰符的ThreadLocal变量。ThreadLocal是Java中用于在每个线程中保存变量副本的类。在这种情况下,listOfSuperPermissionObjectTreeResponse是一个ThreadLocal对象,用于在每个线程中维护一个特定类型的值。 ThreadLocal类的源码中有一个内部静态类Entry,它继承自WeakReference<ThreadLocal<?>>,其中包含了与ThreadLocal对象关联的值。每个ThreadLocal对象都会在Entry中维护一个value,用于存储与当前线程相关联的特定值。当ThreadLocal对象被垃圾回收时,Entry对象也会被回收。 在源码中的ThreadLocal.withInitial(HashMap::new)部分是使用Supplier接口的方法,用于在每个线程中初始化ThreadLocal对象时提供一个初始值。在这种情况下,使用HashMap的构造函数作为初始值供应商。 总之,public static final ThreadLocal listOfSuperPermissionObjectTreeResponse = new ThreadLocal;声明了一个ThreadLocal变量,用于在每个线程中保存一个特定类型的值,并使用HashMap::new作为初始值供应商。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java8源码-ac_babel:一些后端学习笔记整理](https://download.csdn.net/download/weixin_38704485/19390839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [JAVA并发编程--5 理解ThreadLocal](https://blog.csdn.net/l123lgx/article/details/127439245)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值