天天看点

ThreadLocal无锁化线程封闭及与Synchronized的区别

第一、前言

建议将本文和ThreadLocal应用场景-事务案例一起阅读。

第二、ThreadLocal简介

从Java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。

总结:

1.线程并发:在多线程并发的场景下

2.传递数据:我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量

3.线程隔离:每个线程的变量都是独立的,不会相互影响

第三、使用案例

/**
 * 验证线程数据隔离
 * @author shixiancheng
 * 2020-07-25
 */
public class Test {
	private String content;
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public static void main(String [] args){
		final Test test=new Test();
		//开启5个线程
		for(int i=1;i<=5;i++){
			Thread t=new Thread(){
				@Override
				public void run() {
					//每个线程存一个变量,然后取出这个变量
					test.setContent(Thread.currentThread().getName()+"的数据");
					System.out.println("============================");
					System.out.println(Thread.currentThread().getName()+":"+test.getContent());
				}
			};
			t.setName("线程"+i);
			t.start();
		}
	}
}
           

运行结果

============================
线程1:线程1的数据
============================
线程3:线程3的数据
============================
线程5:线程2的数据
============================
线程2:线程4的数据
============================
线程4:线程4的数据
           

从结果可以看出多个线程在访问同一个变量的时候出现的异常,线程间的数据没有隔离。下面我们来看下采用 ThreadLocal 的方式来解决这个问题的例子。

/**
 * 验证线程数据隔离
 * @author shixiancheng
 * 2020-07-25
 */
public class Test {
	private ThreadLocal<String> tl=new ThreadLocal<String>();
	public String getContent() {
		return tl.get();//取出当前线程绑定的变量
	}
	public void setContent(String content) {
		tl.set(content);//将content绑定到当前线程
	}
	public static void main(String [] args){
		final Test test=new Test();
		//开启5个线程
		for(int i=1;i<=5;i++){
			Thread t=new Thread(){
				@Override
				public void run() {
					//每个线程存一个变量,然后取出这个变量
					test.setContent(Thread.currentThread().getName()+"的数据");
					System.out.println("============================");
					System.out.println(Thread.currentThread().getName()+":"+test.getContent());
				}
			};
			t.setName("线程"+i);
			t.start();
		}
	}
}
           

运行结果

============================
============================
线程4:线程4的数据
============================
线程2:线程2的数据
线程1:线程1的数据
============================
线程3:线程3的数据
============================
线程5:线程5的数据
           

从结果来看,这样很好的解决了多线程之间数据隔离的问题,十分方便。

其实加锁也可以实现线程安全

/**
 * 验证线程数据隔离
 * @author shixiancheng
 * 2020-07-25
 */
public class Test {
	private String content;
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public static void main(String [] args){
		final Test test=new Test();
		//开启5个线程
		for(int i=1;i<=5;i++){
			Thread t=new Thread(){
				@Override
				public void run() {
					synchronized(Test.class){
						//每个线程存一个变量,然后取出这个变量
						test.setContent(Thread.currentThread().getName()+"的数据");
						System.out.println("============================");
						System.out.println(Thread.currentThread().getName()+":"+test.getContent());
					}
				}
			};
			t.setName("线程"+i);
			t.start();
		}
	}
}
           

运行结果

============================
线程1:线程1的数据
============================
线程3:线程3的数据
============================
线程4:线程4的数据
============================
线程2:线程2的数据
============================
线程5:线程5的数据
           

从结果可以发现, 加锁确实可以解决这个问题,但是在这里我们强调的是线程数据隔离的问题,并不是多线程共享数据的问题, 在这个案例中使用synchronized关键字是不合适的。

第四、ThreadLocal与synchronized的区别

ThreadLocal无锁化线程封闭及与Synchronized的区别

第五、ThreadLocal方案的好处

从上述的案例中可以看到,在一些特定场景下,ThreadLocal方案有两个突出的优势:

1.传递数据:保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题。

2.线程隔离:各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失。

欢迎大家积极留言交流学习心得,点赞的人最美丽,谢谢