高并发学习(二)安全发布对象/不可变对象/线程封闭

线程安全的途径:

安全发布对象-发布与逃逸

  • 发布对象:使一个对象能够被当前范围之外的代码所使用
  • 对象逃逸:一种错误的发布。当一个对象还没有构造完成的,就使它被其他线程所见 

安全发布对象(单例模式:列一下)

单例模式中的枚举方式是最安全的,双重锁的时候加volitale关键字才是线程安全的。

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型或者AtomicReference对象中
  • 将对象的引用保存到某个正确构造对象的final类型域中
  • 将对象的引用保存到锁保护的域中

躲避并发(1.不可变对象2.线程封闭)

不可变对象

  • 对象创建之后其状态就不能修改
  • 对象所有域都是final类型
  • 对象是正确创建的(在创建期间,this引用没有逃逸)

final关键字:类、方法、变量

修饰类:不能被继承

修饰方法:1、锁定方法不能被继承类修改;2、效率

修饰变量:基本数据类型变量、引用类型变量

其他不可变对象:(多线程情况下不会有线程安全的问题了)

Collections.unmodeifiableXXX:Collection,List,Set,Map,,,

Guava:ImmutableXXX:Collection、List、Set、Map

线程封闭

  1. Ad-hoc线程封闭:程序控制实现,最糟糕,忽略
  2. 堆栈封闭:局部变量,并发无问题(不会被多个线程共享,全局的变量容易引起并发的问题)
  3. ThreadLocal线程封闭:特别好的封闭方法(内部维护了map)

threadlocal(看源码):

线程局部变量,ThreadLocal实例通常来说都是private static类型的。ThreadLocal可以给一个初始值,而每个线程都会获得这个初始值的一个副本,这样就能保证不同的线程都有一个拷贝(每个拷贝之间相互没有影响)

解决:

1.多线程中相同变量的访问冲突问题。

2.数据库连接,先判断connThreadLocal.get()是否为null,如果是null,则说明当前线程还灭有对应的Connection对象,这时创建一个Coonection对象并添加到本地线程变量中;如果不为null,则说明当前线程已经拥有了Connection对象,直接使用就可以了。

//其中public Object get()该方法返回当前线程所对应的线程局部变量。

preHandler

afterHandler

线程不安全的类的写法:

Stringbuilder类:不安全

StringBuffer类:近乎所有方法都加了sychronized的方法

SimpleDateFormat类:不安全

 DateTime类(joda-time):安全

同步容器:

ArrayList(不同步)-》Vector,Stack(同步)

HashMap-》HashTable(key、value不能为null)(同步)

Collections.synchronizedXXX(List、Set、Map)(同步)

eg.对集合中的元素进行删除,不建议在foreach循环和利用iterator的过程中进行删除,建议先进行标记,标记之后再进行删除。

并发容器JUC:

ArrayList->CopyOnWriteArrayList(写的使用开辟内存(复制),且用了lock锁)

  • 缺点:1.内存2.不能实时读,只能最终一致性(适合读多写少)
  • 性质:读写分离,最终一致性,写的时候开辟空间

HashSet、TreeSet->CopyOnWriteArraySet

ConcurrentSkipListSet

HashMap、TreeMap->ConcurrentHashMap(效率是ConcurrentSkipListMap的4倍)

ConcurrentSkipListMap(key有序,支持比ConcurrentHashMap并发性更高)

原文地址:https://www.cnblogs.com/nickup/p/9695386.html