ThreadLocal
思想:ThreadLocal通过让每一个线程复制一份变量,使得每个线程对变量进行操作时实际上是操作自己本地内存里面的副本,从而避免了对共享变量进行同步。
作用:每一个ThreadLocal能够放一个线程级别的变量,可是它本身能够被多个线程共享使用,并且又能够达到线程安全的目的,且绝对线程安全。
使用:
public class ThreadLocalTest {
static void print(String str){
//打印当前线程本地内存中的localVariable变量值
System.out.println(str + ":"+ localVariable.get());
//清除当前线程本地内存中的localVariable的值
//localVariable.remove();
}
//会给每个访问该变量的线程设置一个副本,线程都值访问自己的本地副本
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {
Thread threaOne = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1的本地localVariable值
localVariable.set("threadOne local variable");
print("threadOne");
System.out.println("threadOne remove after " + " : " +localVariable.get());
}
});
Thread threaTwo = new Thread(new Runnable() {
@Override
public void run() {
//设置线程2的本地localVariable值
localVariable.set("threadTwo local variable");
print("threadTwo");
System.out.println("threadTwo remove after " + " : " +localVariable.get());
}
});
threaOne.start();
threaTwo.start();
}
}
实现原理:
Thread类中有一个ThreadLocalMap类型的threadLocals和inheritableThreadLocals变量,这个Map是一个定制的HashMap,默认情况都为null,到调用ThreadLocal的get和set方法时才会创建它们。也就是说,其实每个线程的本地变量不是存放在ThreadLocal里面的,而是存在调用线程的threadLocals里面,ThreadLocal相当于一个工具类,把值设置到调用线程的threadLocals里面去
ThreadLocal不支持继承性
子线程设置了ThreadLocal变量,父亲线程获取为空,因为不同线程,无法访问,父亲访问的是自己线程中的ThreadLocal变量,但是因为没有调用set或者get方法,所以没有被初始化。
使用 static ThreadLocal<String> localVariable = new InheritableThreadLocal<>(); 即可
InheriableThreadLocal继承ThreadLocal类,通过获取父亲进程,然后获取其ThreadLocal变量,更新到自己本地的inheriableThreadLocals中,不再使用threadLocals
使用场景:
InheritableThreadLocal:子线程需要获取父线程变量时,如子线程要获取threadLocals里面的用户登录信息,比如一些中间件要把统一的id跟踪的调用链路记录下来。
ThreadLocal:在一些数据库连接,session连接中能用,装饰者模式,最好运用在一个全局的设计上,清楚什么时候使用它,什么时候回收它,结合线程池用可能会炸裂(容易忘记回收,所有一定一定要慎重使用,Spring事务中的使用了ThreadLocal,有空要去看看是怎么实现的)
ThreadLocalRandom
Random类在多线程下,多个线程都拿一个老的种子取计算新的种子,导致种子一样,失去随机性,因此新的Random类使用CAS操作去更换老的种子,保证了多个线程在如果是同一个新种子的时候,只有一个线程能更换成功,更换失败的线程会用新的种子重新计算。
但是这又带来了一个问题:每个Random实例里面都有一个原子性的种子变量用来记录当前种子值,当要生成新的随机数的时候,多个线程会来竞争同一个原子变量的更新操作,由于更新时用的是CAS操作,只有一个线程会成功,这会造成大量线程进行自旋重试,降低并发性能。ThreadLocalRandom就是为了减少这大量的自旋重试操作
ThreadLocalRandom使用ThreadLocal原理让每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算,并且更新种子,这样就不会存在竞争了。这个种子放在具体调用Thread的threadLocalRandomSeed里,调用ThreadLocalRandom.current()就是初始化这个threadLocalRandomSeed和threadLocalRandomProbe变量(通过Unsafe的本地方法)。
CopyOnWriteArrayList
1、是一个线程安全的ArrayList,使用了写时复制策略。
2、对其操作都是在底层的一个复制的数组(快照)上进行的 ,修改完后再替换原来的数组,使用ReentrantLock独占锁
3、初始化:空参构造方法构建,默认新建的是一个长度为0的数组 ; 也可以传入一个具体数组,这样可以直接构建一个新的固定长度的数组
4、添加\修改\删除元素:加锁,然后新建一个原数组拷贝,接着修改完成后替换回去,释放锁
5、迭代器遍历一致性:这里的实现具有弱一致性 ; 定义了一个final 的数组,赋予原数组引用,这时候如果遍历过程原数组修改了,这时候迭代器的数组保持不变,CopyOnWriteArrayList里面的修改为新的
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyWriteArrayListTest {
private static volatile CopyOnWriteArrayList<String> arrayList = new
CopyOnWriteArrayList<>();
public static void main(String[] args) throws InterruptedException {
arrayList.add("hello");
arrayList.add("alibaba");
arrayList.add("welcome");
arrayList.add("to");
arrayList.add("hangzhou");
Thread one = new Thread(new Runnable() {
@Override
public void run() {
arrayList.set(1,"baba");
arrayList.remove(2);
arrayList.remove(3);
}
});
Iterator<String> it = arrayList.iterator(); //先获取迭代器
one.start(); //再删除部分元素
one.join(); //等待线程执行完
while (it.hasNext()){
System.out.println(it.next());
}
}
}