【并发编程】线程安全策略
线程封闭
线程封闭
把对象封装到一个线程里,只有这个线程能看到这个对象。
实现线程封闭
Ad-hoc 线程封闭:程序控制实现,最糟糕,忽略
堆栈封闭:局部变量,无并发问题
ThreadLocal 线程封闭:特别好的封闭方法
ThreadLocal 实例保存登录用户信息
public class RequestHolder {
private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();
/**
* 添加数据
* 在filter里将登录用户信息存入ThreadLocal
* 如果不使用ThreadLocal,我们会需要将request一直透传
* @param id
*/
public static void add(Long id){
// ThreadLocal 内部维护一个map,key为当前线程名,value为当前set的变量
requestHolder.set(id);
}
/**
* 获取数据
* @return
*/
public static Long getId(){
return requestHolder.get();
}
/**
* 移除变量信息
* 如果不移除,那么变量不会释放掉,会造成内存泄漏
* 在接口处理完以后进行处理(interceptor)
*/
public static void remove(){
requestHolder.remove();
}
}
线程不安全的类与写法
1.StringBuilder 线程不安全,StringBuffer线程安全
原因:StringBuffer几乎所有的方法都加了synchronized关键字。
2.SimpleDateFormat
SimpleDateFormat 在多线程共享使用的时候回抛出转换异常,应该才用堆栈封闭在每次调用方法的时候在方法里创建一个SimpleDateFormat。
另一种方式是使用joda-time的DateTimeFormatter(推荐使用)。
3.ArrayList,HashMap,HashSet等Collections
4.先检查再执行
// 非原子性
if(condition(a)){
handle(a);
}
同步容器
现在使用的比较少,主要使用并发容器。
ArrayList->Vector,Stack
HashMap->HashTable
Collections.synchronizedXXX (list,set,map)
并发容器
J.U.C下的工具类:【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构
ArrayList->CopyOnWriteArrayList
HashSet/TreeSet->CopyOnWriteArraySet/ConcurrentSkipListSet
HashMap/TreeMap->ConcurrentHashMap/ConcurrentSkipListMap
安全共享对象策略
安全地共享对象包括以下几种策略:
线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改。
共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它。
线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它。
被守护对象:被守护对象只能通过获取特定的锁来访问。
参考资料:
慕课网高并发实战(六)- 线程安全策略