ThreadLocal深入理解 修订版

网友投稿 576 2022-09-25

ThreadLocal深入理解 修订版

ThreadLocal深入理解 修订版

本文是传智博客多线程视频的学习笔记

ThreadLocal是一个和线程安全相关的类。

它能干什么? 能保证在一个线程内,某个变量的全局共享。

说的很模糊,咱们看一个图

线程1里面的数据,应该在线程1范围内的模块a,b,c都能访问。 线程2里面的数据,应该在线程3范围内的模块a,b,c都能访问。 且线程1,2之间数据不会混淆。 那它有什么用呢? 举个例子,银行的转账包含两步,存款和取款,时候如果在存款取款中间出了问题,就得回滚;如果一切正常等整个交易完成了再commit,而调用commit的对象是Connection。那你说,如果多个线程共用一个Connection会发生什么问题?

一个非线程安全的例子

在我们讲述ThreadLocal之前,我们先看一个例子。

import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadScopeDataShare { static private int data = 0; public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 2; i++) { threadPool.execute(new Runnable() { @Override public void run() { int data2= new Random().nextInt(100); System.out.println(Thread.currentThread().getName()+" put "+data2); data=data2; try { Thread.sleep(1000); //为什么要睡1秒 大家懂吗? } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new A().get(); new B().get(); } }); } threadPool.shutdown(); } public static int getData() { return data; }}class A { public int get() { int data = ThreadScopeDataShare.getData(); System.out .println("a "+Thread.currentThread().getName() + " getdata " + data); return data; }}class B { public int get() { int data = ThreadScopeDataShare.getData(); System.out .println("b "+Thread.currentThread().getName() + " getdata " + data); return data; }}

在我们设想中,应该是线程1放的数据,在线程1中,模块A与模块B取得的数据应该是一致的。同理,线程2里面放的数据,工作再线程2下的模块A模块B取得的数据也应该是一致的。 可是上面的代码的运行结果却是: pool-1-thread-1 put 90 pool-1-thread-2 put 78 a  pool-1-thread-2 getdata 78 b  pool-1-thread-2 getdata 78 a  pool-1-thread-1 getdata 78 b  pool-1-thread-1 getdata 78

改进版

我们新建一个map,key是当前线程,value是我们要保存的数据。 那么就可以保证每个线程的各个模块取得的数据都是一致的。

public class ThreadScopeShareData3 { private static Map threadData = new HashMap(); public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 2; i++) { threadPool.execute(new Runnable() { @Override public void run() { int data2= new Random().nextInt(100); System.out.println(Thread.currentThread().getName()+" put "+data2); threadData.put(Thread.currentThread(),data2); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new A().get(); new B().get(); } }); } threadPool.shutdown(); } static class A{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } //省略class B}

运行结果

pool-1-thread-1 put 2 pool-1-thread-2 put 99 A from pool-1-thread-2 get data :99 A from pool-1-thread-1 get data :2 B from pool-1-thread-1 get data :2 B from pool-1-thread-2 get data :99

ThreadLocal的简单介绍

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。   当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 我们先看应用再讲原理,然后再讲一个实际的应用。 第一个应用

public class ThreadLocalTest { private static ThreadLocal x = new ThreadLocal(); 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(500); System.out.println(Thread.currentThread().getName() + " has put data :" + data); x.set(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = x.get(); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ int data = x.get(); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } }}

Thread-0 has put data :67 A from Thread-0 get data :67 B from Thread-0 get data :67 Thread-1 has put data :221 A from Thread-1 get data :221 B from Thread-1 get data :221 完全符合我们的要求。 这里有个问题: 如果一个线程能要共享多个变量怎么做? private static ThreadLocal x = new ThreadLocal(); private static ThreadLocal y = new ThreadLocal(); 不嫌麻烦吗? ThreadLocal里可以放Interger,也可以放Objcet么。 如果几个变量有关系,如name,age我们就把它们包装成User;

如果变量没有关系,那就包装成一个map。

(当然一个线程如果要共享多个变量,那么分别设置为x,y也是可以的)

这样可以不?

import java.util.Random;public class ThreadLocalTest3 { private static ThreadLocal myThreadScopeData = new ThreadLocal(); 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(500); MyThreadScopeData2 myData = new MyThreadScopeData2(); myData.setName("name" + data); myData.setAge(data); myThreadScopeData.set(myData); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { MyThreadScopeData2 myData = myThreadScopeData.get(); System.out .println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } //省略class B}class MyThreadScopeData2 { private static ThreadLocal map = new ThreadLocal(); private String name; private int age; //省略get set}

可以,不过对用户来说暴露了ThreadLocal的应用,我们希望在调用的时候,ThreadLocal对用户是透明的。 换句话说,我们得把ThreadLocal包装起来。

import java.util.Random;public class ThreadLocalTest2 { 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(500); MyThreadScopeData.getThreadInstance().setName("name" + data); MyThreadScopeData.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } //省略Class B}class MyThreadScopeData{ private MyThreadScopeData(){} public static MyThreadScopeData getThreadInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; } private static ThreadLocal map = new ThreadLocal(); private String name; private int age; //省略getset}

关于上面的单例模式可以参考​

现在重头戏来了,看看ThreadLocal实现的原理。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:樊振东、陈梦单打折桂,WTT新加坡大满贯国乒包揽5冠!
下一篇:如何理解构建人类命运共同体思想的科学内涵?
相关文章

 发表评论

暂时没有评论,来抢沙发吧~