上篇博客针对并发和线程进行了一些概括性的讲解,说到了我们需要利用线程进行并发编程,虽然有一定难度,但是有很大的好处。那么这篇博客就针对难度来说,说说线程带来了哪些风险,以至于导致进行并发编程难度加大。
在java平台中,线程是一部分非常重要的内容,java对线程提供了相应的语言和库,以及一种跨平台的内存模型(关于内存模型推荐看马士兵讲师介绍的java内存模型),这种内存模型实现了开发中编写一次随处运行的并发应用程序。利用java平台简化了并发程序的开发,但是线程也带来了许多风险,对开发人员技术要求也提高了。
关于线程带来的风险应该从这样三点来看,安全性问题、活跃性问题和性能问题。
安全性问题其实就是线程安全性,这一点是非常复杂的,因为在没有同步的情况下,多个线程同时执行,执行顺序是不可预测的,可能会出现奇怪的结果。下面举个例子来说线程安全问题,并且简单分析会出现怎样的结果。
public class Sequ
{
private int value;
public int getNext()
{
return value++;
}
}
比较简单的java程序,本质是执行一次得到的value值加1,类似于逐渐生成器的这样一个类。那么如果是在多线程中这样的代码程序就会出现多个线程获得的值是相同的。
从上面的执行流程能够看出多线程访问这样一个程序代码会出现不同线程得到的值相同,这种安全性问题成为竞态条件。出现这种情况是由于多个线程共享相同的内存地址空间,并且是并发的,所以可能会出现线程正在访问其他线程正在使用的变量,这种方式可以实现数据共享,但是带来了风险,因为线程无法预料数据而产生错误。那么在java平台中其实有一个关键字来消除这种安全性问题就是synchronized,这个后面咱们具体再来讨论。
关于安全性问题其实就是多个线程同时访问和修改相同变量的时候,将原先串行的编程模型加入了非串行性,这种非串行性是非常难分析的。要想多线程行为可以预测就必须对共享数据进行同步,当然可以用synchronized进行加锁操作,这样就在线程之间消除了彼此的干扰,安全性问题就是使用线程引入的,带来的风险也应该引起开发人员的注意。
活跃性问题,关注的是某件正确事情最终会发生。当某个操作无法继续执行下去的时候就会出现活跃性问题。活跃性问题在串行和并行中都会出现。
首先说串行中出现这种活跃性问题表现的形式之一就是我们常见的死循环,出现死循环之后循环之后的代码就会无法得到正确执行。
再说并发,由于线程的引入,会出现A线程在等待线程B释放其持有的资源,而B线程永远都不释放该资源,那么A就永久的无法执行。
活跃性问题也是线程引入进行并发编程需要注意的,关于怎么避免这些问题后面会有深入的讨论。
最后就是性能问题,其实活跃性关注的是某件正确的事情最终会发生,而性能问题关注的是正确的事情尽早发生。性能其实包括很多内容,例如服务器响应时间、吞吐量,资源消耗过高等。性能问题也不仅仅是串行仅有的,在并发程序中也需要注意。
如果是良好的并发应用设计,线程能够提升性能。这需要对并发编程人员有很高的要求,因为无论如何线程总会带来某种运行时的开销。
在多线程中,线程调度器临时挂起活跃线程转而运行另一个线程就会出现上下文切换,会保存和恢复执行上下文,让cpu会开销在线程调度上而不是运行商。所以线程引入也会带来性能上的风险,关于如果减小一些开销提高多线程的性能后面也会有介绍。
这篇文章主要是介绍一下多线程下的并发编程会带来几种风险,也是由于这几种风险增加了并发编程的难度,而且如果想充分利用好并发编程带来效益和收益的话,这几方面内容是不能忽视的。有了这些认识,下面会有介绍如何针对这些风险进行规避,而且我也相信充分认识和看待这些风险才能更加好的学习多线程和并发编程。