《JAVA 并发编程实战》 第一章:简介

一、并发简史

早期的计算机不包含操作系统,从头到尾只执行一个程序,对于昂贵并且稀有的计算机资源来说是一种浪费。

操作系统的出现使得计算机能运行多个程序,并且不同的程序都在单独的进程中运行:操作系统为各个独立执行的进程分配各种资源,包括内存。如果需要的话,在不同的进程之间可以通过一些粗颗粒度的通信机制来交换数据,包括:套接字、信号处理器、共享内存、信号量以及文件等。

之所以在计算机中加入操作系统来实现多个程序的同时执行,主要是基于以下原因:

  • 资源利用率:等待的同时可以运行另一个程序
  • 公平性:不是由一个程序从头到尾
  • 便利性:更容易实现

串行编程模型的优势在于直观性和简单性,每次只做一件事情,做完之后再做另一件。然而并不高效,例如在烤面包的时候,可以去进行其他事情,直到面包烤完,进行通知。但凡做事高效的人,总能在串行性和异步性之间找到合理的平衡,对于程序来说同样如此。

二、线程的优势

如果使用得当,线程可以有效地额降低程序的开发和维护等成本,同时提升复杂应用程序的性能。线程能够将大部分的异步工作流转换成串行工作流,因此能更好的模拟人类的工作方式和交互方式。此外,线程还可以降低代码的复杂度,使代码更容易编写、阅读和维护。

2.1 发挥多处理器的强大能力

由于基本的调度单位是线程,因此如果程序中只有一个线程,那么最多同时只能在一个处理器上运行。 多线程可以同时在多个处理器上执行。如果设计正确,多线程可以通过提高资源利用率来提升系统的吞吐率。

2.2 建模的简单性

完成单任务时,很简单,只要把这件事做好就行。但是在完成多任务的时候,不仅要把活干好,还要考虑不同任务之间存在优先级和时间。

2.3 异步事件的简化处理

一部分线程接受客户端请求,另一部分处理器请求。如果是单线程程序的话,处理请求时会停顿导致接受请求阻塞,为了避免这个问题单线程程序必须使用非阻塞的I/O,这种I/O的复杂性要远远高于同步I/O。

三、线程带来的风险

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

3.1 安全性问题

线程的安全性可能是非常复杂的,在没有充足的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生奇怪的结果。以下的程序会产生一个整数值序列。

因为value++ 不是一个原子性的操作:①读取value;②给value+1;③将值写入value。
由于运行时可能有多个线程同时执行了读操作,他们写入时就会发生冲突,从而影响到结果。

3.2 活跃性问题

开发并发模块时,一定要注意线程安全性是不可破坏的。安全性不仅对多线程很重要,对于单线程同样重要。此外,线程还会导致一些在单线程程序中不会出现的问题,例如活跃性问题。

安全性的含义是“永远不发生糟糕的事情”,而活跃性则关注另一个目标:“某件正确的事情最终会发生”。当某个操作无法继续执行下去的时候,就会发生活跃性问题。

具体问题和解决方法可见第十章:避免活跃性危险

3.3 性能问题

与活跃性问题关系密切的是性能问题。活跃性意义着某件正确的事情最终会发生,但是不够好,因为我们通常希望正确的事情尽快发生。

性能问题包括多方面:

  • 服务时间过长
  • 响应不灵敏
  • 吞吐率过低
  • 资源消耗过高
  • 可伸缩性较低

在设计良好的并发程序中,线程能提高程序的性能,但是无论如何,线程总会带来某种程度的运行开销。在多线程中,当线程调度器临时挂起活跃性线程并转而运行另一个线程的时候,会频繁的出现上下文切换操作,带来很大的开销:保存和恢复执行上下文,丢失局部性,并且CPU时间将更多的花在线程调度而不是线程运行上。当线程共享数据时,必须使用同步机制,而这些机制往往会抑制某些编译器优化,使得内存缓存区中的数据无效,以及增加共享内存总线的同步流量。

具体问题和解决方法可见第十一章:性能与可伸缩性

3.4 线程无处不在

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值