第一章——简介

最近在看Java并发编程方面的东西,据说《Java并发编程实践》这本书http://book.douban.com/subject/10484692/不错,打算将读书心得记录下来,与大家一起分享。之前看豆瓣的评价,说中文版翻译的很差,所以下载了英文版,后来看机械工业出版社出版的这个翻译还好吧,就买了一本,大家可以自己决定哈~~

1.1并发简史

早期的计算机没有操作系统,都是一个程序运行到底再执行第二个程序,这对于计算机资源是很浪费的。后来出现了操作系统,通过引入进程的概念,同时可以执行多个任务,主要因为如下原因:


  1. 资源利用率:一个程序因为等待输入而暂停,另外的程序可以先使用CPU,提高利用率
  2. 公平性:所有的程序都应该公平地使用计算资源,不能由一个程序一直占着,等它用完了再给别的程序使用
  3. 便利性:在每个程序中编写一个任务,然后适当地通信交换结果,比将所有任务都写在一个程序中方便

线程的出现同样是基于以上考虑,线程也被称为轻量级进程。不同的是每个进程相当于一台虚拟的计算机,拥有自己的内存空间,而多个线程会共享进程范围内的资源,例如内存句柄和文件句柄等,但每个线程有各自的程序计数器(Programmer Counter)、栈以及局部变量。

1.2线程的优势

发挥多处理器的能力

现在提高单个CPU的主频已经越来越难,生产厂商开始转而生产更多核的CPU,现在连手机都有双核甚至四核的了。但是如果同时只有一个线程在执行,则多余的CPU会闲置,浪费了资源。

建模的简单性

将每个单独的任务放在一个线程中执行,然后在适当地时机进行交互,可以简化程序的开发。比如Servlet将每个用户请求单独放置在一个线程中进行响应,因此在service方法中不必考虑同时有多少请求需要处理。

异步事件的简化处理

如果只有一个线程,则如果某个I/O阻塞会影响整个程序的执行,因此往往采用非阻塞的I/O,但是这套机制复杂性较高。而如果创建多个线程,则一个线程的I/O阻塞并不会影响其他线程,整个程序还是可以运行下去。现代操作系统允许的线程数量得到了很大的提升,因此给每个客户端配置一个线程是可行的。

更灵敏的GUI响应

将用户的操作放置在一个事件线程中执行,可以避免程序执行一个耗时很长的操作导致程序“卡死”。

1.3线程带来的风险

安全性问题

在单线程中能安全执行(得到预期的结果)的程序在多线程下可能得到错误的结果。比如下面的代码


@NotThreadSafe
public class UnsafeSequence {
    private int value;

    /** Returns a unique value. */
    public int getNext() {
        return value++;
    }
}
预期每次调用返回唯一的一个整数值,但是如果两个线程恰好按照某种特定的顺序执行,可能得到错误的结果,如下图



第一章——简介


A、B两个线程得到的结果是一致的,因此这个程序在多线程环境下是不安全的。可以通过同步修复该错误,


@ThreadSafe
public class Sequence {
    @GuardedBy("this") private int nextValue;

    public synchronized int getNext() {
        return nextValue++;
    }
}


活跃性问题

安全性指的是“永远不发生糟糕的事情”,而活跃性指的是“某件正确的事最终会发生”。在串行程序中,活跃性问题之一是无限循环,而并发程序中有死锁、饥饿以及活锁等。并发错误很难分析,因为bug并不是必现的。

性能问题

包括响应时间、吞吐率、资源消耗、可伸缩性等。

多线程程序中涉及到上下文切换(Context Switch),会有很大的开销。同时线程调度也会占用CPU。而为了获得线程安全,需要使用同步机制,而这抑制了编译器的某些优化。因此如果多线程实现不好,可能反而会降低程序的性能。

1.4线程无处不在

即使自己不直接使用多线程,但是使用的很多框架(比如Timer、Serlvet/JSP、RMI以及Swing和AWT等)也会使用,这会将并发引入到自己的程序中,因此也必须了解。

框架通过在框架线程中调用应用程序的代码将并发性引入到程序中。在该代码中如果访问某些应用程序状态,则所有访问这些状态的代码路径都必须是线程安全的。比如Servlet中service是多线程执行的,如果其中访问到了某个状态A,那么程序中的其他访问状态A的代码都必须相应地进行同步。


你可能感兴趣的:(第一章——简介)