1 threadLocal:存值时再以当前 ThreadLocal 实例对象为 key,这样即使同一线程中,不同 ThreadLocal 虽然使用同一个容器,但 key 不一样,取值时也就不会相互影响。key使用WeakReference 存储,在get 或者set 的时候如果key==null的时候,就可以认为这个值无效了,就会去清理。
泄露的场景仅且仅在:
线程run方法结束后没有显示的调用remove进行清理
线程在线程池的模式下,一直重复运行。
2.volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就这这个操作同样存在线程安全问题。
原子操作的三个原则::原子性,有序性和可见性
synchronized: 具有原子性,有序性和可见性;
synchronized的三种使用方式
修饰实例方法,为当前实例加锁,进入同步方法前要获得当前实例的锁。
修饰静态方法,为当前类对象加锁,进入同步方法前要获得当前类对象的锁。
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。
synchronized的等待唤醒是通过notify/notifyAll和wait
synchronized的底层实现主要依靠Lock-Free的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量
volatile:具有有序性和可见性
synchronized这种独占锁属于悲观锁,乐观锁最常见的就是CAS(当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。可见 CAS 其实是一个乐观锁):
我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。
比较 A 与 V 是否相等。(比较)
如果比较相等,将 B 写入 V。(交换)
返回操作是否成功。