线程是Java语言中不可或缺的重要功能,可以使复杂的异步代码变得简单,简化复杂系统开发。
要想充分发挥多核处理器的强大计算能力,最简单的方式就是使用线程。

并发简史

早期的计算机不包含操作系统,从头到尾只执行一个程序,并且这个程序能访问计算机中的所有资源。

计算机加入操作系统实现多线程的原因

  • 资源利用率
    某些情况下,程序必须等待某个外部操作执行完成,在等待的同时可以运行另一个程序,可提高资源的利用率
  • 公平性
    高效的运行方式是通过通过粗粒度的时间分片使这些用户和程序能够共享计算机资源,而不是由一个程序从头运行到尾,然后再启动下一个程序
  • 便利性
    每个程序执行一个任务并在必要时相互通信,比只编写一个程序来计算所有任务更容易实现

串行性和异步性

串行模型优势在于其直观性和简单性,因为其模仿了人类的工作方式:每次只做一件事情,做完之后再做另一件。
对于程序来说,需要在串行性和异步性之间找到合理的平衡

线程被称为轻量级进程,会共享进程范围内的资源(内存句柄和文件句柄,但每个线程都有各自的程序计数器、栈及局部变量)。
如果没有明确的同步机制来协同对共享数据的访问,当一个线程正在使用某个变量时,另一个线程可能同时访问这个变量,会造成不可预测的结果。

线程的优势

有效降低程序开发和维护成本,同时提升发杂应用程序的性能,更好地模拟人类的工作方式和交互方式,降低代码的复杂度,使代码更容易编写、阅读和维护。
在GUI应用程序中,线程可以提高用户界面的响应灵敏度,在服务器应用中,可以提升资源利用率以及系统吞吐量,简化JVM的实现,垃圾收集器通常在一个或多个专门的线程中运行。许多重要的Java应用程序中,都在一定程度上用到了线程

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

  • 基本的调度单位是线程,因此如果在程序中只有一个线程,最多只能在一个处理器上运行。
  • 多线程可以同时在多个处理器上执行,提高处理器资源的利用率来提升系统吞吐率
    多线程也有助于单处理器系统上获得更高的吞吐率,丽日在I/O阻塞期间可以继续运行

建模的简单性

如果程序中只包含一种类型的任务,比包含多种不同类型的任务的程序要更易于编写,错误更少,也更容易测试。
为模型中每种类型的任务都分配一个专门的线程,那么可以形成一种串行执行的假象,并将程序的执行逻辑与调度机制的细节,交替执行的操作,异步I/O以及资源等待问题分离开来

异步事件的简化处理

服务器应用程序在接受来自多个远程客户端的套接字连接请求时,如果为每个连接都分配其各自的线程并且使用同步I/O,会降低开发难度

响应更灵敏的用户界面

传统的GUI应用程序通常为单线程,在代码的各个位置都需要调用poll方法来获得输入时间,或者通过一个“主事件循环”来间接地执行应用程序的所有代码。如果在主事件循环中调用的代码需要很长时间才能执行完成,那么用户界面就会“冻结”,直到代码执行完成。
如果将这个长时间运行的任务放在一个单独的线程中运行,那么事件线程就能及时地处理界面事件,使用户界面具有更高的灵敏度

线程带来的风险

提高了对开发人员地技术要求,并发性是一个“高深地”主题

安全性问题

在没有充足同步地情况下,多个线程中的操作执行顺序时不可预测的,会产生奇怪的结果
由于多个线程要共享相同的内存地址空间,并且是并发运行,因此可能会访问或修改其他线程正在使用的变量,更容易实现数据共享,但是线程会由于无法预料的数据变化而发生错误,Java提供了各种同步机制来协同这种访问

@NotThreadSafe
public class UnsafeSequence {
    private int value;

    //返回一个唯一的数值
    public int getNext() {
        return value++;
    }
@ThreadSafe
//将getNext修改为一个同步方法可以修复线程不安全的错误
public class Sequence {
    @GuardedBy("this")
    private int Value;

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

活跃性问题

在某个操作无法继续执行下去时,就会发生活跃性问题。
在串行单线程程序中,活跃性问题之一就是无限循环,循环后的代码无法得到执行
多线程并发中,A等待B释放其持有的资源,而B永远都不释放资源,A会永远地等待下去

性能问题

多线程总会带来某种程度的运行时开销。会频繁地先换上下文操作带来极大的开销:保存和恢复执行上下文,丢失局部性,CPU会发非更多的事件在线程调度上而不是运行上,同步机制会抑制某些编译器优化,使得缓存区中的数据无效等

线程无处不在

即使在程序中没有显式地创建线程,但在框架中仍可能会创建线程,因此在这些线程中调用的代码同样必须是线程安全的。

  • 每个Java应用程序都会使用线程
    JVM启动时,为JVM内部任务(垃圾收集、终结操作等)创建后台线程,创建一个主线程来运行main方法。
    AWT和Swing的用户界面框架将创建线程来管理用户界面事件。
    一些组建框架,Servlet和RMI会创建线程池并调用这些线程中的方法

框架通过在框架线程中调用应用程序代码将并发性引用到程序中。在代码中将不可避免地访问应用程序状态,因此所有访问这些状态的代码路径都必须是线程安全的



这个家伙很懒,啥也没有留下😋