1、ThreadLocal初识
从Java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。
我们可以得知ThreadLocal的作用是∶提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。
- 线程并发:ThreadLocal只在多线程的环境下起作用
- 传递数据:我们可以通过ThreadLocal在同一线程不同组件中传递公共数据
- 线程隔离:每一个线程的变量都是独立的,不会相互影响
2、ThreadLocal的基本使用
(1)常用方法
(2)ThreadLocal的简单使用
多线程的不隔离性:
public class T1 { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public static void main(String[] args) { T1 t1=new T1(); for(int i=0;i<5;i++){ Thread thread=new Thread(new Runnable() { @Override public void run() { t1.setContent(Thread.currentThread().getName()+"的数据:"); System.out.println(Thread.currentThread().getName()+"---"+t1.getContent()); } }); thread.setName("线程"+i); thread.start(); } } }
测试结果:
线程1---线程1的数据: 线程0---线程1的数据: 线程2---线程2的数据: 线程3---线程3的数据: 线程4---线程4的数据:
将变量与当前线程绑定:
public class T1 { private String content; ThreadLocal<String> threadLocal=new ThreadLocal<>(); public String getContent() { return threadLocal.get(); } public void setContent(String content) { threadLocal.set(content); } public static void main(String[] args) { T1 t1=new T1(); for(int i=0;i<5;i++){ Thread thread=new Thread(new Runnable() { @Override public void run() { t1.setContent(Thread.currentThread().getName()+"的数据:"); System.out.println(Thread.currentThread().getName()+"---"+t1.getContent()); } }); thread.setName("线程"+i); thread.start(); } } }
测试结果:
线程0---线程0的数据: 线程1---线程1的数据: 线程2---线程2的数据: 线程3---线程3的数据: 线程4---线程4的数据:
多个线程的数据是相互隔离的。在多线程的场景下,每一个线程的变量都相互独立。例如:线程1设置的变量是变量1,那么获取到的变量也是变量1。
(3)加锁
public class T1 { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public static void main(String[] args) { T1 t1=new T1(); for(int i=0;i<5;i++){ Thread thread=new Thread(new Runnable() { @Override public void run() { synchronized (T1.class){ t1.setContent(Thread.currentThread().getName()+"的数据:"); System.out.println(Thread.currentThread().getName()+"---"+t1.getContent()); } } }); thread.setName("线程"+i); thread.start(); } } }
虽然ThreadLocal模式与synchronized关键字都用于处理多线程并梭访问变量的问题,不过两者处理问题的角度和思路不同。
用ThreadLocal可以使得程序有更好的性能,因为ThreadLocal没有加锁
(4)案例
在银行转账的案例中:
传统解决方案:
保证service层和dao层的连接对象的一致,可以通过将service层的参数传递到dao层,但是会提高代码的耦合度
需要保证每一个线程的连接对象前后一致,也就是说线程1的连接对象对应连接1,线程2的连接对象对应连接2,可以通过加锁(synchronized)的方式保证线程的隔离,但是会降低程序的性能
ThreadLocal方式:
传递数据︰保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题
线程隔离︰各线程之间的数据相互隔离却又具备并发性,避兔同步方式带来的性能损失