线程安全
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程安全相关的知识,希望对你有一定的参考价值。
什么叫线程安全
一个类是线程安全的是指多个线程同时访问该类时,该类都表现出正确的行为。
线程安全的因素:原子性,可见性。原子性是指一系列操作要么不执行,要么全部执行。破坏原子性的操作主要存在两个地方:竞态条件,复合操作。竞态条件,是指某个操作的结果的正确性依赖于线程的执行顺序。为了保证原子性,必须采用加锁机制。可见性,可见性是指一个线程修改某个变量时,其他线程能看到变量的变化,如果没有同步将无法实现这一点。可见性没有满足时会带来失效数据(过时的数据或者错误的数据),可见性的保证通过加锁和轻量的volatile变量来实现,volatile变量满足变量的写操作必须在读操作之前的happen_before顺序,通过内存屏障实现。
前面讲述的是如何保证一个对象内部域的线程安全性,一个对象被共享时如何保证它的线程安全呢?当然一个对象能不共享最好不共享了,可以通过线程封闭的方法来实现,常见的TreadLocal。如果非共享不可,首先要保证该对象是安全发布的,针对不变对象怎么发布都行,针对事实不变对象需要安全发布,针对可变对象需要安全发布,且必须有锁保护起来。安全发布一个对象可以通过静态初始化函数初始化一个引用,volatile变量声明或者AtomicRefrence变量声明,存放到线程安全容器中,将对象的引用保存到一个锁保护的域中。提醒一点,安全的共享对象最基本的前提是该对象引用没有逃逸。在实际编码过程中,设计一个线程安全类时可能有时候不需要考虑这么多,可以将线程安全性委托给现有的线程安全基础模块,比如同步容器类,并发容器类,阻塞队列,或者通过使用同步工具类来实现一个线程安全类。
同步容器类:HashTable,Vector,Sychronizedxxx。这些类实现线程安全的方法,是对每个状态变量的访问都进行加锁,这种的实现方式的弊端是每次都只能有一个线程访问该容器,其余线程将阻塞,且不保证复合操作(先判断在检查,迭代)的同步,必须得客户端额外加锁(加的锁必须是该容器的对象锁)。其中迭代器的是非线程安全的,当迭代过程又线程修改容器时迭代器将爆出ConcurrentModifyException。
并发容器:concurrentHashmap,copyOnWriteArrayList,BlockQueue。
同步工具类:CountdownLatch,semophere,FutureTask,CyclicBarrier。CountdownLatch提供两个方法await,countDown,初始化时闭锁维护一个计数器,运行时线程将阻塞在await方法上,直到闭锁的计数器值为0,其中闭锁的计数器的状态由countDown方法递减,计数器的值不可重置。通俗的来讲就是线程在等待一个事件的发生,当该事件发生时各个线程就各干各的。semaphore提供两个方法acquire(),release()方法,初始化时信号量维护一个计数器,运行时线程将阻塞在acquire方法上,直到信号量的值为0,执行线程任务,release()信号量,它跟闭锁的区别是技术器可重复使用。CycliBarrier的语意是线程将阻塞,直到所有的线程都到达栅栏位置,线程释放,重置栅栏。CyclicBarrier只有一个await方法,线程将阻塞在该方法上直到所有的线程都到达栅栏,若其中一个线程阻塞在await方法上,但是阻塞超时或者阻塞中断,栅栏将打破,并抛出异常。
以上是关于线程安全的主要内容,如果未能解决你的问题,请参考以下文章