发布对象
发布对象:使一个对象能够被当前范围之外的代码所使用
对象逸出:一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见
安全发布对象四种方法
1 在静态初始化函数中初始化一个对象引用
2 将对象的引用保存到volatile类型域或者AtomicReference对象中
3 将对象的引用保存到某个正确构造对象的final类型域中
4 将对象的引用保存到一个由锁保护的域中
私有构造函数,单例对象,静态工厂方法获取对象
以单例模式为例
懒汉模式:单例实例在第一次使用时进行创建(线程不安全)
懒汉模式也可以实现线程安全,给getInstance方法添加synchronized关键字(不推荐,因为性能不好)
双重同步锁单例模式:双重监测机制,在方法内部加synchronized关键字(不是线程安全的)
原因是,创建对象是,分为以下三个步骤:
1) memory = allocate() 分配对象的内存空间
2)ctorInstance() 初始化对象
3)instance = memory() 设置instance指向刚分配的内存
由于JVM和cpu优化,可能会发生指令重排:
1) memory = allocate() 分配对象的内存空间
3) instance = memory() 设置instance指向刚分配的内存
2) ctorInstance() 初始化对象
当以上面这种指令执行时,线程A执行到3 instance = memory() 设置instance指向刚分配的内存 这一步时,线程B执行if(instance == null)这段代码,此时instance != null,线程B直接return instance,导致对象没有初始化完毕就返回
解决办法就是限制对象创建时进行指令重排,volatile+双重监测机制->禁止指令重排引起非线程安全
饿汉模式:单例实例在类装载时进行创建(线程安全)
枚举模式:线程安全
线程不安全类与写法
字符串
StringBuilder:线程不安全
StringBuffer:线程安全
时间转换
SimpleDateFormat:线程不安全
JodaTime:线程安全
集合
ArrayList:线程不安全
HashSet:线程不安全
HashMap:线程不安全
同步容器(在多线程环境下不推荐使用)
ArrayList -> Vector, Stack
Vector中的方法使用synchronized修饰过
Stack继承Vector
HashMap -> HashTable(key、value不能为null)
HashTable使用synchronized修饰方法
Collections.synchronizedXXX(List、Set、Map)
同步容器不完全是线程安全的
编程注意:如果使用foreach或者iterator遍历集合时,尽量不要对集合进行修改操作
并发容器J.U.C(java.util.concurrent)(在多线程环境下推荐使用)
ArrayList -> CopyOnWriteArrayList:相比ArrayList,CopyOnWriteArrayList是线程安全的,写操作时复制,即当有新元素添加到CopyOnWriteArrayList时,先从原有的数组里拷贝一份出来,然后在新的数组上写操作,写完之后再将原来的数组指向新的数组,CopyOnWriteArrayList整个操作都是在锁(ReentrantLock锁)的保护下进行的,这么做主要是避免在多线程并发做add操作时复制出多个副本出来,把数据搞乱了。第一个缺点是做写操作时,需要拷贝数组,就会消耗内存,如果元素内容比较多会导致youngGC或者是fullGc;第二个缺点是不能用于实时读的场景,比如拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到的数据可能还是旧的,虽然CopyOnWriteArrayList能够做到最终的一致性,但是没法满足实时性要求,因此CopyOnWriteArrayList更适合读多写少的场景
CopyOnWriteArrayList设计思想:1读写分离 2最终一致性 3使用时另外开辟空间解决并发冲突
HashSet -> CopyOnWriteArraySet
TreeSet -> ConcurrentSkipListSet
CopyOnWriteArraySet:底层实现是CopyOnWriteArrayList
ConcurrentSkipListSet:和TreeSet 一样支持自然排序,基于map集合,但是批量操作不是线程安全的
HashMap -> ConcurrentHashMap :不允许空值,针对读操作做了大量的优化,具有特别高的并发性
TreeMap -> ConcurrentSkipListMap :内部使用SkipList跳表结构实现的,key是有序的,支持更高的并发
安全共享对象策略——总结
1 线程限制:一个呗线程限制的对象,由线程独占,并且只能被占有它的线程修改
2 共享只读:一个共享只读的对象,在没有额外的同步情况下,可以被多个线程并发访问,但是任何线程都不能修改它
3 线程安全对象:一个线程安全的对象或容器,在内部通过同步机制来保证线程安全,所以其他线程无序额外的同步就可以通过公共接口随意访问它
4 被守护对象:被守护对象只能通过获取特定的锁来访问
不可变对象、线程封闭、同步容器、并发容器