DNA图谱 / 问答 / 问答详情

JAVA 中的同步机制有什么作用?

2023-07-01 09:47:12
共2条回复
meira

Java中的同步机制有四种:① ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile

目的:都是为了解决多线程中的对同一变量的访问冲突

(1)ThreadLocal 保证不同线程拥有不同实例,相同线程一定拥有相同的实例,即为每一个使用该变量的线程提供一个该变量值的副本,每一个线程都可以独立改变自己的副本,而不是与其它线程的副本冲突。

(2)优势:提供了线程安全的共享对象

(3)与其它同步机制的区别:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信;而 ThreadLocal 是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源,这样当然不需要多个线程进行同步了。

(4)使用技巧:需要多个线程之间进行通信,使用同步机制;如果需要隔离多个线程之间的共享冲突,推荐使用 ThreadLocal (线程安全)。

天线宝宝说害怕

一种是方法前加sychronized

public void sychronized start() {

System.out.println("start");

}

另一种是在代码段之前加sychronized

(sychronized){

。。。。。

}

同步方法(synchronized关键字修饰的方法)可以较好地解决并发问题,在一定程度上可以避免出现资源抢占、竞争条件和死锁的情况,但其副作用是同步锁可导致线程阻塞。这要求同步方法的执行时间不能太长。

这就是所谓的锁机制,你何以使用sychronized(Object obj)锁住某个对象,等你使用完这个对象之后,再进行锁的释放,其他需要该对象的线程才可以执行。

相关推荐

并发编程-Threadlocal

上一篇 <<< Fork/Join框架 下一篇 >>> Disruptor框架 Threadlocal: 各个线程独有的局部变量,相互之间不受影响。 它主要有四个方法initialValue()、get()、set()和remove(),底层采用了map集合形式进行存放,key为当前线程ID。 不管是用强引用还是弱引用都是会发生内存泄漏的问题。弱引用中不会发生ThreadLocal内存泄漏的问题。 但是最终根本的原因Threadlocal内存泄漏的问题,产生于ThreadLocalMap与我们当前线程的生命周期一样,如果没有手动的删除的情况下,就有可能会发生内存泄漏的问题。 相关文章链接: <<< 多线程基础 <<< 线程安全与解决方案 <<< 锁的深入化 <<< 锁的优化 <<< Java内存模型(JMM) <<< Volatile解决JMM的可见性问题 <<< Volatile的伪共享和重排序 <<< CAS无锁模式及ABA问题 <<< Synchronized锁 <<< Lock锁 <<< AQS同步器 <<< Condition <<< CountDownLatch同步计数器 <<< Semaphore信号量 <<< CyclicBarrier屏障 <<< 线程池 <<< 并发队列 <<< Callable与Future模式 <<< Fork/Join框架 <<< Disruptor框架 <<< 如何优化多线程总结
2023-07-01 08:25:441

java的ThreadLocal是否能被其他线程访问到

threadlocal是针对 per-thread的,不会被其他线程并发访问。 threadlocal变量和单个线程关联,简单来讲,类似于(具体可参考jdk源码): Java代码 public class FakeThreadLocal<T> { private final Map<Thread, T> values = new HashMap<Thread, T>(); public T get() { return values.get(Thread.currentThread()); } public void set(T t) { values.put(Thread.currentThread(), t); } } 应用场景1,创建一个 SimpleDateFormat实例的开销比较昂贵,解析字符串时间时频繁创建生命周期短暂的实例导致性能低下。即使将 SimpleDateFormat定义为静态类变量,貌似能解决这个问题,但是SimpleDateFormat是非线程安全的,同样存在问题,如果用 ‘synchronized"线程同步同样面临问题,同步导致性能下降(线程之间序列化的获取SimpleDateFormat实例)。 使用Threadlocal解决了此问题,对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本。 Java代码 public class Test { // SimpleDateFormat is not thread-safe, so give one to each thread private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); } } 应用场景2,在servlet/filter中保存初始状态,以便解决并发问题。 应用场景3,在一些框架中保存和当前线程有关的上下文。
2023-07-01 08:25:502

ThreadLocal的使用方法是什么?

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。    ThreadLocal是什么    ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。    ThreadLocal的设计    首先看看ThreadLocal的接口:     Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值     void set(Object value); // 设置当前线程的线程局部变量副本的值    ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:   protected Object initialValue() { return null; }   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:   public class ThreadLocal   {    private Map values = Collections.synchronizedMap(new HashMap());    public Object get()    {    Thread curThread = Thread.currentThread();    Object o = values.get(curThread);    if (o == null && !values.containsKey(curThread))    {     o = initialValue();     values.put(curThread, o);    }    return o;    }    public void set(Object newValue)    {    values.put(Thread.currentThread(), newValue);    }    public Object initialValue()    {    return null;    }   }   当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。    ThreadLocal的使用    如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:   public class SerialNum   {    // The next serial number to be assigned    private static int nextSerialNum = 0;    private static ThreadLocal serialNum = new ThreadLocal()    {    protected synchronized Object initialValue()    {     return new Integer(nextSerialNum++);    }    };    public static int get()    {    return ((Integer) (serialNum.get())).intValue();    }   }   SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:   int serial = SerialNum.get();   即可。    在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。    ThreadLocal与其它同步机制的比较    ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。    由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。    总结    当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
2023-07-01 08:25:571

ThreadLocal共享线程局部变量和线程同步机制的区别

  ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。  对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。  ThreadLocal 并不能替代同步机制,两者面向的问题领域不同。  1:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;  2:而threadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享变量,这样当然不需要对多个线程进行同步了。  import java.util.Random;public class ThreadSocpeShareData { static ThreadLocal<Integer> t = new ThreadLocal<Integer>(); public static void main(String[] args) { for(int i=0;i<3;i++){ new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() +" has put "+ data); t.set(data); MyThreadScopeData.getInstance().setName("name" + data); MyThreadScopeData.getInstance().setAge("age"+data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = t.get(); MyThreadScopeData myData = MyThreadScopeData.getInstance(); System.out.println("A " + Thread.currentThread().getName() +" "+ data + myData.getAge() + myData.getName() /*ms.getName()*/); } } static class B{ public void get(){ int data = t.get(); System.out.println("B " + Thread.currentThread().getName()+ " "+ data); } }}class MyThreadScopeData{ private MyThreadScopeData(){} private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); public static MyThreadScopeData getInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; }private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; }}  事实上,我们向ThreadLocal中set的变量不是由ThreadLocal来存储的,而是Thread线程对象自身保存。当用户调用ThreadLocal对象的set(Object o)时,该方法则通过Thread.currentThread()获取当前线程,将变量存入Thread中的一个Map内,而Map的Key就是当前的ThreadLocal实例。请看源码,这是最主要的两个函数,能看出ThreadLocal与Thread的调用关系:  public void set(T value) {    Thread t = Thread.currentThread();      ThreadLocalMap map = getMap(t);  if (map != null)        map.set(this, value);  else      createMap(t, value);  }  ThreadLocalMap getMap(Thread t) {  return t.threadLocals;  }   public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }  具体可以看看里面的源码,不过有一点是可以证实的,就是Threadlocal中 创建的线程副本,可以不调用remove来做清理工作,因为jvm在发现线程的调佣不再使用时,会进行自动的垃圾回收操作,我以前写程序在使用Threadlocal时却是经常的进行使用完成之后的清理工作。(防止占用内存,如果创建的副本数量不是太多的话,可以让虚拟机自动来清除)
2023-07-01 08:26:041

什么是ThreadLocal?解决了什么问题?

首先加深印象: 1.ThreadLocal解决的是每个线程需要有自己独立的实例,且这个实例的修改不会影响到其他线程。 这个ThreadLocal的用法一般都是创建各自线程自己运行过程中单独创建的对象的,不适合的相同实例共享的。 https://ask.csdn.net/questions/764337 ThreadLocal存入的对象就不该是同一个。这玩意不保证线程安全。 所谓的副本是指,A线程存入的值,对B线程并不感知,B只能拿到自己存的值,并不能拿到A存入的值。 因为一般情况下ThreadLocal 都是定义为static类型的,如果没有ThreadLocal,那么B线程就可以获取A线程所存入的值。 理解了使用场景,很多地方就能想通了 2.ThreadLocal造成内存泄漏的问题我认为有两点: ThreadLocal原理总结 1.每个Thread维护着一个ThreadLocalMap的引用 2.ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储 3.调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象 4.调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象 5.ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。 这里放出一个程序可以思考一下会输出什么? 答案是 可以debug来看一下他的流程 第一次set,由于Thread中的map还没有被初始化,所以先创建map 因为第一个ThreadLocal已经初始化了map,所以map!=null为true, 到此为止,上面的程序也好理解了,
2023-07-01 08:26:111

什么是ThreadLocal?

  ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。  ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。  ThreadLocal的接口方法:  ThreadLocal类接口很简单,只有4个方法,先来了解一下:  void set(Object value)  public void remove()  将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。  protected Object initialValue()  返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。  值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。  ThreadLocal为每一个线程维护变量的副本的思路:  在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
2023-07-01 08:26:192

Java:ThreadLocal究竟有什么用呢?费解

从如下8点来讲解一下:1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型) void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值 void remove() 移除此线程局部变量当前线程的值 protected T initialValue() 返回此线程局部变量的当前线程的“初始值” T get() 返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理: ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal: public class SimpleThreadLocal{ private Map valueMap=Collections.synchronizedMap(new HashMap()); public void set(Object newValue){ valueMap.put(Thread.currentThread(),newValue);//键为线程对象,值为本线程的变量副本 } public Object get(){ Thread currentThread=Thread.currentThread(); Object o=valueMap.get(currentThread);//返回本线程对应的变量 if(o==null&&!valueMap.containsKey(currentThread)){ //如果在Map中不存在,放到Map中保存起来 o=initialValue(); valueMap.put(currentThread,o); } return o; } public void remove(){ valueMap.remove(Thread.currentThread()); } public void initialValue(){ return null; } }8.使用ThreadLocal的具体例子: public class SequenceNumber{ //通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seNum=new ThreadLocal<Integer>(){ protected Integer initialValue(){ return 0; } } public Integer getNextNum(){ seNum.set(seNum.get()+1); return seNum.get(); } public static void main(String[] args){ SequenceNumber sn=new SequenceNumber(); //3个线程共享sn,各自产生序列号 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread{ private SequenceNumber sn; public TestClient(SequenceNumber sn){ this.sn=sn; } public void run(){ //每个线程打印3个序列号 for(int i=0;i<3;i++){ System.out.println("thread["+Thread.currentThread().getName()+",sn["+sn.getNextNum()+"]"); } } } }
2023-07-01 08:26:273

ThreadLocal本质及脏数据、内存泄漏问题

ThreadLocal作为WeakReference的referent,只要ThreadLocal对象引用被置为null,Entry的key(referent)就会在下一次YGC时被回收。在使用ThreadLocal的get()和set()时,会将失效的Entry(key == null)的value置为null,使value能够被GC回收,避免内存泄漏。 ThreadLocal对象常作为私有静态变量使用,其生命周期不会随着线程结束而结束。 ThreadLocal本质属性:线程间共享。 调用该init的有8个Thread构造器: 这8个构造器的inheritThreadLocals为true。 只有下面的构造器inheritThreadLocals为false: 在init中: InheritableThreadLocal用来给子线程从父线程继承thread-local值。 InheritableThreadLocal自动与线程的inheritableThreadLocals域关联起来。 childValue()是个protected方法,其用在子线程创建时,用来从父线程copy thread-local值时,可以设置初始值。 使用ThreadLocal和InheritableThreadLocal透传上下文时,需要注意线程间切换、异常传输时的处理,避免在传输过程中因处理不当而导致的上下文丢失。 ThreadLocal对象本身由多线程共享。 而T类型的值各线程各不相同。 例如SimpleDateFormat是线程不安全的,可以放到ThreadLocal<DateFormat>中,然每个线程单独有一份SimpleDateFormat对象。 主要问题是会产生脏数据和内存泄漏。通常是在线程池的线程中使用ThreadLocal引发的,因为线程池有线程复用和内存常驻两个特点。 run()中没有显式地调用remove()清理与线程相关的ThreadLocal信息,下一个线程也没有set()设置初始值,则会产生脏数据。 ThreadLocal一般是static对象。因此寄希望于ThreadLocal对象失去引用后,触发弱引用回收机制来回收Value是不现实的。 为了防止内存泄漏,每次用完ThreadLocal,必须要及时调用remove()方法清理。
2023-07-01 08:26:341

什么是threadlocal变量

ThreadLocal变量就是和线程绑定的变量.实际上是一个Map,,,key是对应的线程,值则是该变量. 调用ThreadLocal的get方法时则会到Map中查询当前线程是否已拥有该变量,如果没有则新建一个并保存到Map中.有的话直接返回与该线程绑定的变量. 说白了就是
2023-07-01 08:26:422

ThreadLocal是如何实现保存线程私有对象的

最早知道ThreadLocal是在Looper的源码里,用一个ThreadLocal保存了当前的looper对象。 当时就查了下ThreadLocal,发现是保存线程私有对象的容器,以为就是一个类似hashmap,用线程做key,存value。最近看了下并不是如此,实际上是以ThreadLocal自身为key来存储对象的。于是来学习下ThreadLocal源码是如何做到保存线程私有对象的。 ThreadLocal、ThreadLocalMap、Thread之间的关系和我们下意识中想的不太一样,不过一步步看下去之后就能明白为啥ThreadLocal能保存线程私有对象了。 这个是Thread源码,每一个Thread都有一个ThreadLocalMap对象,而ThreadLocalMap是ThreadLocal的内部类,是实际存储对象的容器。 基本关系知道了,最后再来看看ThreadLocal的方法: 那么现在基本完全清楚ThreadLocal与Thread还有ThreadLocalMap之间的关系了。 每个Thread都有一个成员变量ThreadLocalMap。这个ThreadLocalMap对象不是public,所以外部调用不了,可以看做是线程私有对象。 ThreadLocalMap存了一个table,table里保存了一些entry,每个entry对应一个key和value,而这个key就是ThreadLocal对象。 因此一个ThreadLocal只能存一个value,但是可以通过new多个ThreadLocal来保存多个线程私有对象。 在上面的源码中我们看到Entry里持有的ThreadLocal对象是弱引用持有,因此ThreadLocal不会因为线程持有而泄露,比如我们Android的主线程,正常使用过程中是不会挂掉的。 但是Enrty的value的是强引用的,因此ThreadLocal中的value还是会因为线程持有而无法回收。如果继续看源码的话,会发现在ThreadLocalMap的resize和expungeStaleEntry方法里会检查key为空的value值为空帮助GC。 因此为了避免value内存泄露,我们需要在ThreadLocal不需要的时候主动remove掉。 ThreadLocal通过自身的threadLocalHashCode来碰撞得到自己在ThreadLocalMap的table里的索引i。因此这个threadLocalHashCode就十分重要了。 这里需要保证threadLocalHashCode是唯一的,否则两个线程同时创建ThreadLocal得到相同的hashcode,那就破坏了ThreadLocalMap的线程私有特性了。 这里生成threadLocalHashCode是通过一个静态对象nextHashCode不断增加来获得的。那怎么保证多个进程并发实例化ThreadLocal对象,不会生成相同的hashcode呢? 答案是AtomicInteger,通过这个类来保证变量自增操作在多线程操作时候的原子性。 我们知道Synchronized也可以保证原子性,但相比于它,AtomicInteger类是通过非阻塞方法来实现原子性的,需要的性能开销更小。 这种非阻塞实现原子性的方法和处理器的CAS指令有关,感兴趣的小伙伴自行研究吧~
2023-07-01 08:26:491

Java:ThreadLocal究竟有什么用呢?费解

1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型)void set(T value)将此线程局部变量的当前线程副本中的值设置为指定值void remove()移除此线程局部变量当前线程的值protected T initialValue()返回此线程局部变量的当前线程的“初始值”T get()返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理:ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal:1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型)void set(T value)将此线程局部变量的当前线程副本中的值设置为指定值void remove()移除此线程局部变量当前线程的值protected T initialValue()返回此线程局部变量的当前线程的“初始值”T get()返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理:ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal:
2023-07-01 08:27:092

ThreadLocal 方式存储用户信息

因为每次业务层 或者 其他非 Controller 层,需要用到用户信息的时候,就需要把当前用户信息先查询出来在传递,或者是直接把当前的 session 直接往下层传递。这样很繁琐。 可以使用 线程封闭 性来优化这个问题。 1.每次请求的时候,可能不是同一个线程去执行,会到导致用户数据获取不到。 2.线程要释放绑定的用户数据,不然会出现内存泄露的问题。 先创建 ThreadLocal 操作类 用户session操作类,针对 ThreadLocal 操作二次封装 定义一个 Filter 解决,每次请求不是同一个线程,造成用户信息获取不到问题 定义一个 Request 上下文监听器,用于释放线程绑定的用户信息,避免内存泄露 这里也可以选择 Spring Mvc提供的拦截器。但是我这里选择用 Servlet 的原生接口实现 看看结果: Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。下面将介绍几种常用的监听器,以及它们都适合运用于那些环境。 分类及介绍: 1. ServletContextListener :用于监听WEB 应用启动和销毁的事件,监听器类需要实现javax.servlet.ServletContextListener 接口。 2. ServletContextAttributeListener :用于监听WEB应用属性改变的事件,包括:增加属性、删除属性、修改属性,监听器类需要实现javax.servlet.ServletContextAttributeListener接口。 3. ServletRequestListener : 用于监听请求的创建于销毁, 需要实现 javax.servlet.ServletRequestListeneru200b 接口 4. ServletRequestAttributeListener :请求属性事件监听器。用于监听请求中的属性改变的事件。 5. HttpSessionListener :用于监听Session对象的创建和销毁,监听器类需要实现javax.servlet.http.HttpSessionListener接口或者javax.servlet.http.HttpSessionActivationListener接口,或者两个都实现。 6. HttpSessionActivationListener :用于监听Session对象的钝化/活化事件,监听器类需要实现javax.servlet.http.HttpSessionListener接口或者javax.servlet.http.HttpSessionActivationListener接口,或者两个都实现。 7. HttpSessionAttributeListener :用于监听Session对象属性的改变事件,监听器类需要实现 8. HttpSessionBindingListener :会话值绑定事件监听器。这是唯一不需要在web.xml中设定的Listener。 9. Filter :用于在客户端的请求访问后端资源之前,拦截这些请求 或者 在服务器的响应发送回客户端之前,处理这些响应
2023-07-01 08:27:161

ThreadLocal父子线程数据传递解决方案

在阅读前,您需要对ThreadLocal用法有一定了解。如果不了解,请参考ThreadLocal用法相关文章。 这个需求或许也很常见,最开始,我们可能会想到InheritableThreadLocal,这是由Jdk为我们提供,他是ThreadLocal的子类,使用方法和ThreadLocal完全相同。 但是这里有几个坑: 1、手动通过线程池创建线程可能会造成get值为null。 2、项目中我们往往会使用线程池,如果主线程使用的是缓存线程池(比如SpringMvc),线程会复用,当线程执行完毕后本次操作后,再次执行新的任务时候,ThreadLocal内部数据并没有被清除。 3、ThreadLocal父子线程之间数据拷贝默认是浅拷贝,这也就意味如果我们多个线程可能会引用同一个内存地址,造成多个线程访问一个对象,轻者会造成线程不安全,重者甚至会ThreadLocal数据被修改成非预期结果。 这些坑本人亲身体验过,血的教训总结出来,那么如何很好的解决这些问题呢?这边给出一个终级解决方案,使用阿里Transmittable ThreadLocal,可以很好的解决上面这些问题。 具体使用方法可以参见文档: https://github.com/alibaba/transmittable-thread-local 如果使用后发现在缓存线程池下仍然会出现ThreadLocal没有被清理干净的问题,可可尝试重写TransmittableThreadLocal afterExecute方法,在此方法中清除线程数据。 那么通过阿里的TransmittableThreadLocal 如何解决浅拷贝问题呢?这里它提供了一个copy方法,TransmittableThreadLocal 在进行对象拷贝的时候会调用copy方法,我们只需要重写这个方法,让他变量深复制即可。 最后,建议大家不要轻意使用ThreadLocal,能不用尽量不要去使用,一旦在缓存线程池下如果数据没有清理干净会很麻烦,不方便问题排查,而且ThreadLocal有一定情况下会有造成内存泄露的风险,一定要慎用!慎用!慎用!但并不意味着禁用!
2023-07-01 08:27:221

ThreadLocal如何保证一个线程只能有一个Looper?

我们都知道在调用Looper.prepare的时候会创建一个Looper,那么是如何保证一个线程只有一个Looper的? 首先要知道Looper中有一个sThreadLocal变量,ThreadLocal用于存储上下文信息 并且用final static 修饰,所以它是唯一的内容不可变的 了解sThreadLocal是干啥用的后,再来看看prepare 先调用sThreadLocal.get()方法 而ThreadLocalMap 是一个HashMap,那么取到一个HashMap后判断是否为null 如果不存在key为sThreadLocal的节点,得到value = null,并把这个value作为sThreadLocal的值即<sThreadLocal,null>;如果map为null,则创建一个HashMap并把<sThreadLocal,null>节点加入 这样get方法就要么取到一个Looper,要么就是null,如果为Looper则抛异常,如果为null,则调用sThreadLocal.set() 其实都是把Looper作为sThreadLocal的value值 回到开头说的,怎么保证一个线程只有一个Looper? 因为sThreadLocal是线程的上下文,并且唯一,而线程中存有<sThreadLocal,Looper>key-value键值对,所以一个sThreadLocal对应一个Looper,并且再次修改Looper是,会抛异常,因为Looper已经存在。 所以一个线程只有一个Looper。 如果对HashMap还不了解的同学,这篇文章可能对你有一定帮助 HashMap原理
2023-07-01 08:27:291

关于ThreadLocal如何保证数据线程安全

ThreadLocal的核心概念是没一个线程可以通过get或者set方法访问它自己的、独立初始化的变量的副本。1. ThreadLocal介绍为了在多线程环境下不出现任何的冲突,我们希望能否分离一个类的多个实例。对于每一个线程来说,没一个实例都是唯一的。这不过是实现线程安全的一个方式。线程安全的另外的重要的一点是能够全局访问。可以在线程内部的任何地方进行访问。记住:应该声明成static和final的。2. 什么是线程安全线程是进程的一条单独的线。当我们提到多线程应用的时候,我们的意思是一个进程的多个线程访问同一行代码。在这个情况下,存在一个线程访问或者修改另一个线程的数据的可能性。当数据不允许这样共享的时候,我们应该做成线程安全的。实现线程安全的方式有下面几种:· 重入·互斥(同步机制)·ThreadLocal·原子操作3. 使用ThreadLocal(引用Joshua Bloch的一段话,巨抽象,我没看懂,......)我们有一个非线程安全的变量,我们想把它变成线程安全的,可以考虑同步机制把对象封闭到同步块中。另外的方式是使用ThreadLocal,为每一个线程持有单独的对象使得变得安全。4. ThreadLocal的例子package com.javapapers;import java.text.SimpleDateFormat;import java.util.Date;public class ThreadLocalExample {private static final ThreadLocal formatter = new ThreadLocal() {protected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyyMMdd HHmm");}};public String formatIt(Date date) {return formatter.get().format(date);}}上边的代码中,关键要理解get()方法。他返回的当前线程的ThreadLocal变量的副本。如果当前线程的这个变量还没有值,则通过调用initalValue方法返回第一次初始化的值。JavaDoc中的例子下面的例子为每一个线程生成一个唯一的标识符。第一次调用get方法的时候指定一个线程的id,并且在随后的调用中保持不变。port java.util.concurrent.atomic.AtomicInteger;public class ThreadId {// Atomic integer containing the next thread ID to be assignedprivate static final AtomicInteger nextId = new AtomicInteger(0);// Thread local variable containing each thread"s IDprivate static final ThreadLocal threadId =new ThreadLocal() {@Override protected Integer initialValue() {return nextId.getAndIncrement();}};// Returns the current thread"s unique ID, assigning it if necessarypublic static int get() {return threadId.get();}}5. Java API中ThreadLocal 的使用在JDK1.7中,我们有一个叫做ThreadLocalRandom的新类。它用于为并行的线程生成随机数,每一个线程是随机数的种子都是唯一的。这是一个非常酷的功能。下面的代码是上边的类实现ThreadLocal 的代码:private static final ThreadLocal localRandom =new ThreadLocal() {protected ThreadLocalRandom initialValue() {return new ThreadLocalRandom();}};使用ThreadLocalRandompackage com.javapapers;import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomExample {public static void main(String args[]) throws InterruptedException {//tossing 3 coinsfor (int i = 0; i < 3; i++) {final Thread thread = new Thread() {public void run() {System.out.print(Thread.currentThread().getName() + ":");// generating 3 random numbers - random for every threadfor (int j = 0; j < 3; j++) {final int random = ThreadLocalRandom.current().nextInt(1, 3);System.out.print(random + ",");}System.out.println();}};thread.start();thread.join();}}}6. ThreadLocal和内存泄露ThreadLocal不是一个魔鬼,而是一个优秀的、实用的API。完全取决于我们怎么样去使用。我们应该学会在正确的场合使用合适的工具。我们不能使用大炮打蚊子,但是也不能谴责大炮。
2023-07-01 08:27:361

dubbo 中使用 ThreadLocal 隐患

在 Dubbo 中使用 ThreadLocal ,如果采用默认的设置,每次 Dubbo 调用结束,Dubbo 处理响应线程并不会被销毁, 而是归还到线程池中。而从 ThreadLocal 源码可以看出,每次我们设置的值其实会存在位于 Thread 中 ThreadLocalMap 变量中。 这就导致,下次如果 Dubbo 处理响应恰好继续使用到这个线程,该线程就能调用到上次响应中设置在 ThreadLocal 设置的值。这就引起内存泄露,可能还会导致业务上异常。其实并不止在 Dubbo 中,该案例还会发生在 web项目中,只要相关使用线程池的,都有可能发生。 dubbo 默认采用单一长连接加线程池方式处理调用。 默认采取 Dispatcher=all 的分发策略,所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。 线程池在缺省配置为固定大小线程池,启动时建立线程,不关闭,一直持有。 使用 Threadlocal,我们需要注意几点: 调整线程池大小配置是 dubbo.protocol.threads = 5000 调整线程池类型配置是 dubbo.protocol.threadpool = cached 调整事件处理方式配置是 dubbo.protocol.dispatcher = message 或者 <dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
2023-07-01 08:27:421

ThreadLocal 和 Thread 局部变量的区别

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。
2023-07-01 08:27:501

java threadlocal线程结束会释放当前线程的数据吗?

不会清空,要你自己去清空。只有当ThreadLocal的生命周期受限于Task的生命周期时,在Thread Pool的Thread里使用ThreadLocal才有意义。Task指的是一个Thread所执行的任务。总之,如果你能够在使用ThreadLocal的时候管理它的创建、销毁,那么就可以用,否则会出问题。原因是ThreadLocal是和Thread绑定的,如果Thread是从Thread Pool中拿出来的,那么意味着Thread可能会被复用,如果被复用,你就一定得保证这个Thread上一次结束的时候,其关联的ThreadLocal被清空掉,否则就会串到下一次使用。
2023-07-01 08:27:561

c++有threadlocal么

C++有thread_local关键字,不过得在新版本的编译器才能使用。
2023-07-01 08:28:141

如何将ThreadLocal传递到子线程

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。 如果想在不同线程内共享,那么直接用公共静态属性即可,如: public static int pagesize;
2023-07-01 08:28:201

解决线程切换导致ThreadLocal值丢失

1.jdk 自带的InheritableThreadLocal 只针对父子线程 有效,针对线程池失效 2.使用阿里的transmittable-thread-local 可以解决线程池内的threadLocal 问题 只需要修改2个地方,修饰线程池和替换InheritableThreadLocal: 原文地址请点击此处
2023-07-01 08:28:271

什么是线程安全以及threadlocal为什么是线

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。线程安全就是说多线程访问同一代码,不会产生不确定的结果。编写线程安全的代码是依靠线程同步。产生不确定 结果的例子:定义一个共享数据: public static int a = 0;多线程多该共享数据进行修改: private static void plus() { for (int i = 0; i < 10; i++) { new Thread() { public void run() { a++; try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("plus:" + Thread.currentThread().getName() + ": " + a); } }.start(); }}输出结果: plus:Thread-5: 5plus:Thread-2: 5plus:Thread-6: 5plus:Thread-9: 5plus:Thread-1: 6plus:Thread-0: 8plus:Thread-4: 8plus:Thread-3: 10plus:Thread-7: 10plus:Thread-8: 10很明显,在第一次输出a的值的时候,a的值就已经被其他线程修改到5了,显然线程不安全。 产生确定结果的例子:利用synchronized关键字将修改a值的地方和输出的地方上锁。让这段代码在某一个时间段内始终只有一个 线程在执行: private static void plus() { for (int i = 0; i < 10; i++) { new Thread() { public void run() { synchronized (JavaVariable.class) { a++; try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("plus:" + Thread.currentThread().getName() + ": " + a); } } }.start(); }}输出结果: plus:Thread-2: 1plus:Thread-6: 2plus:Thread-7: 3plus:Thread-3: 4plus:Thread-8: 5plus:Thread-4: 6plus:Thread-0: 7plus:Thread-9: 8plus:Thread-5: 9plus:Thread-1: 10结果正确。 为什么threadlocal是线程安全的?首先定义一个ThreadLocal。 private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {protected Integer initialValue() {return 0;}};插一句题外话:为什么threadLocal要被声明成静态的?这是为了多个线程使用同一个ThreadLocal对象。一个线程对应一个ThreadLocal对象,和多个线程使用同一个ThreadLocal对象结果是一样的。详细情况参考链接:http://www.blogjava.net/zhangwei217245/archive/2010/04/24/317651.html接着上面的话题,也许可以这样: private static void plus() throws Exception { for (int i = 0; i < 10; i++) { new Thread() { public void run() { //1 a = threadLocal.get(); a++; //2 threadLocal.set(a); System.out.println("plus:" + Thread.currentThread().getName() + ": " + threadLocal.get()); } }.start(); }}它的结果是怎样呢?全部会输出1。当然,它无法使多个线程共同修改一个值,并且保持这个值递增。因为threadlocal不是做这个的,它是为了隔离数据的共享,而不是像同步机制一样实现线程间的通信。但它的结果是可以确定的,所以它是线程安全的。很明显,在代码“1”的时候,每一个线程都会将threadLocal的初始值0赋值给共享变量a,因为每一个线程从threadLocal.get()拿到的值都是自己threadLocal保存的。所以,就没有所以了。 所以对于共享变量a来讲,每个线程都会首先将自己threadLocal里面的初始值0赋值给a,然后将共享变量a+1,然后将a+1的值设置到自己的ThreadLocalMap中,其他线程就访问不到了。下一个线程来的时候又会将自己threadLocal里面的初始值0赋值给a,然后将 a+1,然后... 如此周而复始。a只是被在0和1之间改来改去,最终放到每一个线程的threadLocal里面的a+1的值就不再共享。
2023-07-01 08:28:341

java中ThreadLocal是哪个包的

JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。
2023-07-01 08:28:411

PageHelper 使用 ThreadLocal 的线程复用问题,你用对了吗?

PageHelper 是较为常用的分页插件,通过实现 Mybatis 的 Interceptor 接口完成对 query sql 的动态分页,其中分页参数由 ThreadLocal 进行保存。 简单的 分页执行过程: 观察上述的执行过程,可以发现,如果在第 1 步和第 2 步 之间发生异常,那么 LOCAL_PAGE 中当前线程对应的 page 参数并不会 remove。 在不使用线程池的情况下,当前线程在执行完毕后会被销毁,这时 当前线程 中的 threadLocals 参数 将会被情况,也就清空 了 LOCAL_PAGE 中 当前线程的 page 参数。 但是如果使用了线程池,当前线程执行完毕,并不会被销毁,而是会将当前线程再次存放到池中,标记为空闲状态,以便后续使用。在后续使用这个线程的时候,由于 线程 的 threadLocals 依旧存在有值,尽管我们在第 1 步时未设置 page 参数,第 3 步 的也能获取到page参数,从而生成 count sql 和 page sql ,从而影响我们的正常查询。 SpringBoot 项目中会使用内置的 Tomcat 作为服务器,而Tomcat会默认使用线程来处理请求,从而便引发了上述问题。 因为Tomcat的线程是用来处理request请求,那么在请求完成时,清空当前线程的threadLocals 属性值,也就是执行 LOCAL_PAGE.remove() 即可。实现方式: 这里使用第二种方式,实现 HandlerInterceptor 接口: 定义配置类,配置类需实现 WebMvcConfigurer 接口完成对于WebMvc的相关配置 ,注册 PageLocalWebInterceptor :
2023-07-01 08:28:471

java ThreadLocal空异常

你在getT1 能获得值 的地方 和getT1 获得的值是null 的地方 都打上一句话 System.out.println(Thread.currentThread());他们输出的线程 肯定不是一个线程然后你打开get () 这个方法会发现,获取的Threadlocal的value 是当前线程的value,意思是 前者是你设置value 的线程 你能获得这个value 后者不是该线程 故得到的value 是null
2023-07-01 08:28:561

ThreadLocal 传递参数的问题

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。如果想在不同线程内共享,那么直接用公共静态属性即可,如:public static int pagesize;
2023-07-01 08:29:141

项目中怎么控制多线程高并发访问

1、首先明确信号量Semaphore的用法,然后新建一个项目,new-->file-->class,随意命名,此处命名为semaphoreDemo。2、首先开始一个线程MyTask,实现接口,然后在其中定义窗口买票的流程,主要有进入,买完了,离开,释放信号量,让下一个进入。3、然后设定一个信号量,主要是执行函数,此处定义窗口个数,定义线程池ExecutorService,循环执行这20个人。4、最后通过main函数调用execute函数进行排队问题,开始排队线程。5、在打印中可以看到两个线程在并发执行,剩下的人处于排队状态,只有上一个执行完了下一个才执行。
2023-07-01 08:29:213

JAVA线程本地变量ThreadLocal和私有变量的区别

ThreadLocal变量 作用域是各自线程内部。私有变量作用域 属于该类的实例。所以, ThreadLocal变量 只用于线程内部共享,是线程安全的。私有变量线程不安全,例如,利用一个Runnable实例启动2个线程,这2个线程就可以共同拥有 私有变量。
2023-07-01 08:30:451

threadlocal存在线程安全问题吗

ThreadLocal是线程安全的,它为解决线程安全问题提供一个很好的思路,通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
2023-07-01 08:30:521

ThreadLocal类为什么要加上static修饰

因为对于非静态成员函数而言,默认情况下,参数列表中都会有一个this指针,例如fun(自定义参数),实际上编译后就变成这样类型:fun(自定义参数,某个类 * this)。这样编译就会出错,多了一个参数,所以这个函数就不能作为线程函数了。加上static修饰之后,类的成员函数就不会加上默认this了,所以符合调用规定。
2023-07-01 08:30:592

threadlocal为什么entry的key要设置成弱引用

首先弱引用在垃圾回收的时候,即使存在弱引用,也会回收引用的对象。 看过threadlocal源码的同学肯定知道,threadlocal内部就是在线程Thread类中new了一个类map结构,然后里面存储数据也是用entry。 看源码会发现entry的key是一个弱引用,为什么要这样设计呢? 不一定会内存泄露的。 现在entry的key对应的实例对象能正常销毁了。那么entry对应的值能正常销毁吗?
2023-07-01 08:31:061

请教一下,Java局部变量为什么一定要用ThreadLocal?

举个例子吧,好比俩售票员卖票使用同步机制(synchronized),相当于只有一个计数器,甲卖票的时候先锁定,卖好了,钱也交完了,计数器+1,再解锁,这功夫要是乙那也有业务,就得等会。只要一方锁定,就必须等所有业务都完事才能解锁(卖票,首款,打印单据blabla)。ThreadLocal,就相当于每人一个计数器,不用担心另一个售票员把我统计的票数改错了。就是这么个用处。
2023-07-01 08:31:211

如何获得thread线程的threadlocals的key值

当前的thread线程是获取不到的,threadLocals的维护在jdk中说明了,是由threadLocal来维护的。如果应该从threadLocal中获取threadLocals的值。如果想从当前的thread线程中获取的话,可以考虑使用缓存;例如,以当前线程唯一属性或id,存入缓存中,当需要用时从缓存中拿。
2023-07-01 08:31:282

ThreadLocal的使用方法是什么?

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。    ThreadLocal是什么    ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。    ThreadLocal的设计    首先看看ThreadLocal的接口:     Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值     void set(Object value); // 设置当前线程的线程局部变量副本的值    ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:   protected Object initialValue() { return null; }   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:   public class ThreadLocal   {    private Map values = Collections.synchronizedMap(new HashMap());    public Object get()    {    Thread curThread = Thread.currentThread();    Object o = values.get(curThread);    if (o == null && !values.containsKey(curThread))    {     o = initialValue();     values.put(curThread, o);    }    return o;    }    public void set(Object newValue)    {    values.put(Thread.currentThread(), newValue);    }    public Object initialValue()    {    return null;    }   }   当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。    ThreadLocal的使用    如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:   public class SerialNum   {    // The next serial number to be assigned    private static int nextSerialNum = 0;    private static ThreadLocal serialNum = new ThreadLocal()    {    protected synchronized Object initialValue()    {     return new Integer(nextSerialNum++);    }    };    public static int get()    {    return ((Integer) (serialNum.get())).intValue();    }   }   SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:   int serial = SerialNum.get();   即可。    在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。    ThreadLocal与其它同步机制的比较    ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。    由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。    总结    当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
2023-07-01 08:31:461

threadlocal set方法是不是线程安全的

ThreadLocal的核心概念是没一个线程可以通过get或者set方法访问它自己的、独立初始化的变量的副本。1. ThreadLocal介绍为了在多线程环境下不出现任何的冲突,我们希望能否分离一个类的多个实例。对于每一个线程来说,没一个实例都是唯一的。这不过是实现线程安全的一个方式。线程安全的另外的重要的一点是能够全局访问。可以在线程内部的任何地方进行访问。记住:应该声明成static和final的。2. 什么是线程安全线程是进程的一条单独的线。当我们提到多线程应用的时候,我们的意思是一个进程的多个线程访问同一行代码。在这个情况下,存在一个线程访问或者修改另一个线程的数据的可能性。当数据不允许这样共享的时候,我们应该做成线程安全的。实现线程安全的方式有下面几种:· 重入·互斥(同步机制)·ThreadLocal·原子操作3. 使用ThreadLocal(引用Joshua Bloch的一段话,巨抽象,我没看懂,......)我们有一个非线程安全的变量,我们想把它变成线程安全的,可以考虑同步机制把对象封闭到同步块中。另外的方式是使用ThreadLocal,为每一个线程持有单独的对象使得变得安全。4. ThreadLocal的例子package com.javapapers;import java.text.SimpleDateFormat;import java.util.Date;public class ThreadLocalExample { private static final ThreadLocal formatter = new ThreadLocal() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); }}上边的代码中,关键要理解get()方法。他返回的当前线程的ThreadLocal变量的副本。如果当前线程的这个变量还没有值,则通过调用initalValue方法返回第一次初始化的值。JavaDoc中的例子下面的例子为每一个线程生成一个唯一的标识符。第一次调用get方法的时候指定一个线程的id,并且在随后的调用中保持不变。port java.util.concurrent.atomic.AtomicInteger;public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread"s ID private static final ThreadLocal threadId = new ThreadLocal() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread"s unique ID, assigning it if necessary public static int get() { return threadId.get(); }}5. Java API中ThreadLocal 的使用在JDK1.7中,我们有一个叫做ThreadLocalRandom的新类。它用于为并行的线程生成随机数,每一个线程是随机数的种子都是唯一的。这是一个非常酷的功能。下面的代码是上边的类实现ThreadLocal 的代码:private static final ThreadLocal localRandom = new ThreadLocal() { protected ThreadLocalRandom initialValue() { return new ThreadLocalRandom(); }};使用ThreadLocalRandompackage com.javapapers;import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomExample { public static void main(String args[]) throws InterruptedException { //tossing 3 coins for (int i = 0; i < 3; i++) { final Thread thread = new Thread() { public void run() { System.out.print(Thread.currentThread().getName() + ":"); // generating 3 random numbers - random for every thread for (int j = 0; j < 3; j++) { final int random = ThreadLocalRandom.current().nextInt( 1, 3); System.out.print(random + ","); } System.out.println(); } }; thread.start(); thread.join(); } }}6. ThreadLocal和内存泄露ThreadLocal不是一个魔鬼,而是一个优秀的、实用的API。完全取决于我们怎么样去使用。我们应该学会在正确的场合使用合适的工具。我们不能使用大炮打蚊子,但是也不能谴责大炮。
2023-07-01 08:32:001

threadlocal有没有继承thread类

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。ThreadLocal的接口方法:ThreadLocal类接口很简单,只有4个方法,先来了解一下:voidset(Objectvalue)publicvoidremove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK1.5新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。protectedObjectinitialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal。API方法也相应进行了调整,新版本的API方法分别是voidset(Tvalue)、Tget()以及TinitialValue()。ThreadLocal为每一个线程维护变量的副本的思路:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
2023-07-01 08:32:061

ThreadLocal 和 Thread 局部变量的区别

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。
2023-07-01 08:32:131

如何获得thread线程的threadlocalmap成员属性

dLocalMap threadlocalmap = getMap(thread); if(threadlocalmap != null) { ThreadLocalMap.Entry entry = threadlocalmap.getEntry(this); if(entry != null)
2023-07-01 08:32:191

ThreadLocal 和 Thread 局部变量的区别

```javapublic static void main(String[] args) {MyThread myThread = new MyThread("oo");new Thread(myThread,"1").start();new Thread(myThread,"2").start();}static class MyThread implements Runnable {private String index;public MyThread(String index) {this.index = index;}//局部变量private Integer num = 0;public void run() {System.out.println("线程" + Thread.currentThread().getName() +index+ "的初始 num:" + num);System.out.println("线程" + Thread.currentThread().getName()+index + "的初始 value:" + value.get());for (int i = 0; i < 10; i++) {num += i;value.set(value.get() + i);}System.out.println("线程" + Thread.currentThread().getName() +index + "的累加 num:" + num);System.out.println("线程" + Thread.currentThread().getName() +index + "的累加 value:" + value.get());}}```结果:```线程 2oo 的初始 num:0线程 1oo 的初始 num:0线程 2oo 的初始 value:0线程 1oo 的初始 value:0线程 2oo 的累加 num:45线程 1oo 的累加 num:90线程 1oo 的累加 value:45线程 2oo 的累加 value:45```结论:自定义私有局部变量 线程不安全,当上述情况时
2023-07-01 08:32:261

一个线程结束时threadlocal里面的池的连接会自己关闭么

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码...
2023-07-01 08:32:331

java threadlocal线程结束会释放当前线程的数据吗?

不会清空,要你自己去清空。只有当ThreadLocal的生命周期受限于Task的生命周期时,在Thread Pool的Thread里使用ThreadLocal才有意义。Task指的是一个Thread所执行的任务。总之,如果你能够在使用ThreadLocal的时候管理它的创建、销毁,那么就可以用,否则会出问题。原因是ThreadLocal是和Thread绑定的,如果Thread是从Thread Pool中拿出来的,那么意味着Thread可能会被复用,如果被复用,你就一定得保证这个Thread上一次结束的时候,其关联的ThreadLocal被清空掉,否则就会串到下一次使用。
2023-07-01 08:32:411

RequestFilter.threadLocal.get().getSession()和request.getSession()的区别?

request.getSession(true):若存在会话则返回该会话,否则新建一个会话。request.getSession(false):若存在会话则返回该会话,否则返回NULL
2023-07-01 08:32:591

threadlocal提供了delete方法

你想问的是threadlocal是否提供了delete方法对吧?没有的。threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,hreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值
2023-07-01 08:33:061

java中threadlocal是线程安全的吗

你可以这样理解:ThreadLocal是线程安全的,session具有唯一性
2023-07-01 08:33:131

java threadlocal 线程结束会释放当前线程的数据吗

after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).官方解释,翻译一下就是:在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
2023-07-01 08:33:212

threadlocal的remove方法会阻塞吗

threadlocal的remove方法不会阻塞。threadlocal调用remove方法会导致业务逻辑通顺,web容器使用了线程池,当一个请求使用完某个线程,该线程会放回线程池被其它请求使用。所以threadlocal的remove方法不会阻塞。
2023-07-01 08:33:281

集群环境下,能用ThreadLocal吗

这个不用多想肯定不会的。1、集群只是对请求一个分发。负载均衡而已。2、一个请求过来,根据分发策略然后进入其中一台服务器。进入这个服务器之后就没有分发策略了。只会在这台服务器执行完这个线程。
2023-07-01 08:33:352

如何将ThreadLocal传递到子线程

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。 如果想在不同线程内共享,那么直接用公共静态属性即可,如: public static int pagesize;
2023-07-01 08:33:411

Spring 单例 多例 线程安全等问题,想请教大家

Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有确保这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。Spring对每个bean提供了一个scope属性来表示该bean的作用域。它是bean的生命周期。我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程。ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal是一个为线程提供线程局部变量的工具类。它的思想也十分简单,就是为线程提供一个线程私有的变量副本,这样多个线程都可以随意更改自己线程局部的变量,不会影响到其他线程。不过需要注意的是,ThreadLocal提供的只是一个浅拷贝,如果变量是一个引用类型,那么就要考虑它内部的状态是否会被改变,想要解决这个问题可以通过重写ThreadLocal的initialValue()函数来自己实现深拷贝,建议在使用ThreadLocal时一开始就重写该函数。ThreadLocal通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
2023-07-01 08:33:482

静态线程池对象不关闭会怎样

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但Spring的DAO模板类并未采用线程同步机制,因为线程同步限制了并发访问,会带来很大的性能损失。此外,通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现难度。那模板类究竟仰丈何种魔法神功,可以在无需同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。ThreadLocal是什么早在JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。线程局部变量并不是Java的新发明,很多语言(如IBM IBM XLFORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。ThreadLocal的接口方法ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:void set(Object value)设置当前线程的线程局部变量的值。public Object get()该方法返回当前线程所对应的线程局部变量。public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是voidset(T value)、T get()以及T initialValue()。ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:// 代码清单1 SimpleThreadLocalclass SimpleThreadLocal {private MapvalueMap = Collections.synchronizedMap(new HashMap());public voidset(Object newValue) {valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本}publicObject get() {Thread currentThread = Thread.currentThread();Object o = valueMap.get(currentThread);// ②返回本线程对应的变量if (o == null &&!valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map// 中保存起来。o = initialValue();valueMap.put(currentThread, o);}return o;}public voidremove() {valueMap.remove(Thread.currentThread());}publicObject initialValue() {return null;}}虽然代码清单9?3这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。一个TheadLocal实例下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法package threadLocalDemo;public class SequenceNumber {//①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值privatestatic ThreadLocal<Integer> seqNum =new ThreadLocal<Integer>() {public Integer initialValue() {return 0;}};//②获取下一个序列值public intgetNextNum() {seqNum.set(seqNum.get() + 1);return seqNum.get();}publicstatic void main(String[] args){SequenceNumber sn = new SequenceNumber();// ③ 3个线程共享sn,各自产生序列号TestClient t1 = new TestClient(sn);TestClient t2 = new TestClient(sn);TestClient t3 = new TestClient(sn);t1.start();t2.start();t3.start();}privatestatic class TestClient extends Thread{private SequenceNumber sn;public TestClient(SequenceNumber sn) {this.sn = sn;}public void run(){for (int i = 0; i < 3; i++) {// ④每个线程打出3个序列值System.out.println("thread[" + Thread.currentThread().getName()+"]sn[" + sn.getNextNum() + "]");}}}}通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:thread[Thread-2] sn[1]thread[Thread-0] sn[1]thread[Thread-1] sn[1]thread[Thread-2] sn[2]thread[Thread-0] sn[2]thread[Thread-1] sn[2]thread[Thread-2] sn[3]thread[Thread-0] sn[3]thread[Thread-1] sn[3]考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。Thread同步机制的比较ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK5.0新的ThreadLocal<T>版本。概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。Spring使用ThreadLocal解决线程安全问题我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9?2所示:图1同一线程贯通三层这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。下面的实例能够体现Spring对有状态Bean的改造思路:代码清单3 TopicDao:非线程安全public class TopicDao {private Connection conn;①一个非线程安全的变量public void addTopic(){Statement stat = conn.createStatement();②引用非线程安全变量…}}由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:代码清单4 TopicDao:线程安全package threadLocalDemo;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;public class SqlConnection {//①使用ThreadLocal保存Connection变量privatestatic ThreadLocal<Connection>connThreadLocal = newThreadLocal<Connection>();publicstatic Connection getConnection() {// ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,// 并将其保存到线程本地变量中。if (connThreadLocal.get() == null) {Connection conn = getConnection();connThreadLocal.set(conn);return conn;} else {return connThreadLocal.get();// ③直接返回线程本地变量}}public voidaddTopic() {// ④从ThreadLocal中获取线程对应的Connectiontry {Statement stat = getConnection().createStatement();} catch (SQLException e) {e.printStackTrace();}}}
2023-07-01 08:33:551