线程安全

什么叫线程安全安全

一个类是线程安全的是指多个线程同时访问该类时,该类都表现出正确的行为。并发

线程安全的因素:原子性,可见性。原子性是指一系列操做要么不执行,要么所有执行。破坏原子性的操做主要存在两个地方:竞态条件,复合操做。竞态条件,是指某个操做的结果的正确性依赖于线程的执行顺序。为了保证原子性,必须采用加锁机制。可见性,可见性是指一个线程修改某个变量时,其余线程能看到变量的变化,若是没有同步将没法实现这一点。可见性没有知足时会带来失效数据(过期的数据或者错误的数据),可见性的保证经过加锁和轻量的volatile变量来实现,volatile变量知足变量的写操做必须在读操做以前的happen_before顺序,经过内存屏障实现。app

前面讲述的是如何保证一个对象内部域的线程安全性,一个对象被共享时如何保证它的线程安全呢?固然一个对象能不共享最好不共享了,能够经过线程封闭的方法来实现,常见的TreadLocal。若是非共享不可,首先要保证该对象是安全发布的,针对不变对象怎么发布都行,针对事实不变对象须要安全发布,针对可变对象须要安全发布,且必须有锁保护起来。安全发布一个对象能够经过静态初始化函数初始化一个引用,volatile变量声明或者AtomicRefrence变量声明,存放到线程安全容器中,将对象的引用保存到一个锁保护的域中。提醒一点,安全的共享对象最基本的前提是该对象引用没有逃逸。在实际编码过程当中,设计一个线程安全类时可能有时候不须要考虑这么多,能够将线程安全性委托给现有的线程安全基础模块,好比同步容器类,并发容器类,阻塞队列,或者经过使用同步工具类来实现一个线程安全类。函数

同步容器类:HashTable,Vector,Sychronizedxxx。这些类实现线程安全的方法,是对每一个状态变量的访问都进行加锁,这种的实现方式的弊端是每次都只能有一个线程访问该容器,其他线程将阻塞,且不保证复合操做(先判断在检查,迭代)的同步,必须得客户端额外加锁(加的锁必须是该容器的对象锁)。其中迭代器的是非线程安全的,当迭代过程又线程修改容器时迭代器将爆出ConcurrentModifyException。工具

并发容器:concurrentHashmap,copyOnWriteArrayList,BlockQueue。ui

同步工具类:CountdownLatch,semophere,FutureTask,CyclicBarrier。CountdownLatch提供两个方法await,countDown,初始化时闭锁维护一个计数器,运行时线程将阻塞在await方法上,直到闭锁的计数器值为0,其中闭锁的计数器的状态由countDown方法递减,计数器的值不可重置。通俗的来说就是线程在等待一个事件的发生,当该事件发生时各个线程就各干各的。semaphore提供两个方法acquire(),release()方法,初始化时信号量维护一个计数器,运行时线程将阻塞在acquire方法上,直到信号量的值为0,执行线程任务,release()信号量,它跟闭锁的区别是技术器可重复使用。CycliBarrier的语意是线程将阻塞,直到全部的线程都到达栅栏位置,线程释放,重置栅栏。CyclicBarrier只有一个await方法,线程将阻塞在该方法上直到全部的线程都到达栅栏,若其中一个线程阻塞在await方法上,可是阻塞超时或者阻塞中断,栅栏将打破,并抛出异常。编码