Java并发原理学习笔记+总结+实战(2)——线程安全性问题

1、线程带来的风险

    java对线程的支持其实是一把双刃剑。虽然java提供了相应的语言和库,以及一种明确的跨平台内存模型,这些工具简化了并发应用程序的开发,但同时也提高的对开发人员的技术要求,因为更多的程序中会使用线程。

1.1 线程的安全性问题

    线程安全性可能是非常复杂的,在没有充分同步的情况下,多个线程中的操作次序是不可预测的,甚至会产生奇怪的结果。

/**
 * UnsafeSequence:线程不安全例子.
 *
 * @author YUSIR
 * @version 2019-03-01
 */
public class UnsafeSequence {
  private int value;

  /**
   * @return 返回一个唯一的数值.
   */
  public int getNext() {
    return value++;
  }
}

    上面的demo中将会产生一个整数值序列,该序列中的每个值都是唯一的。该类在单线程环境中能正常的工作,但是在多线程环境中则不能。

    UnSafeSequence的问题在于,如果执行时机不对,那么两个线程的调用getNext()时会得到相同的值。虽然递增运算value++看上去是单个操作,安踏事实上包含了三个独立操作:读取value,将value+1,将计算结果写入value。由于运行时可能将多个线程之间的操作交替执行,因此这两个线程可能同时进行读操作,从而使它们得到相同的值,并都将其加一,结果就是不同的线程返回了相同的值。

     图中给出了不同线程之间的一种交替执行情况。执行时序按照从左到右的顺序递增,每行表示一个线程的动作。这些交替执行是给出的最糟糕的执行情况(事实上,由于指令重排序的可能,因此实际情况可能会更糟糕),目的是为了说明,如果错误地假设程序中的操作将按照某种特定顺序来执行,那么会存在各种可能的、不可预知的危险。

1.2 活跃性问题

    安全性的含义是“永远不发生糟糕的事情”,二活跃性则关注与另一个目标,即“某件正确的事情最终会发生”。当某个操作无法继续执行下去时,就会发生活跃性问题。在串行程序中,活跃性问题的形式之一就是无意中造成的无限循环,从而使循环之后的代码无法得到执行。线程将带来其他一些活跃性问题。例如,如果线程A在等待线程B释放其所持有的资源时,二线程B永远都不释放该资源,那么A就会永久地等待下去。主要是包括:

  • 死锁
  • 饥饿
  • 活锁

1.3 性能问题

    性能问题包括多个方面,例如服务时间过长,响应不灵敏,吞吐率过低,资源消耗过高,或者可伸缩性较低等。使用多线程技术,在能够提升程序性能的同时,不可避免的也会带来某种程度上的运行时开销。在多线程程序中,当线程调度器临时挂起活跃线程并转而运行另一个线程时,就会频繁地出现上下文切换操作,这种操作将带来极大的开销:保存和恢复执行上下文,丢失局部性,并且CPU时间将跟过地花在线程调度而不是线程运行上。当线程共享数据时,必须使用同步机制,而这些机制往往会抑制某些编译器的优化,是内存缓存区中的数据无效,以及增加共享内存总线的同步流量。

 

 

 

--

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章