Fork me on GitHub

并发编程 基础

注意:所有文章除特别说明外,转载请注明出处.

第四章 Java并发编程基础

[TOC]

4.1 线程

提示:在一个Java程序中,Java程序运行不仅是main()方法运行,而是main线程和多个其它线程的同时运行。

4.1.3 线程优先级

现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当线程的时间片用完了就会发生线程调度,并等待下一次分配。

提示:线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不理会Java线程对于优先级的设定。

4.1.4 线程的状态

1.NEW 初始状态,线程被创建,但是还没有调用start()方法。

2.RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统称作:运行中

3.BLOCKED 阻塞状态,表示线程阻塞干预

4.WAITING 等待状态,表示线程进入进入等待状态,需要等待其它线程做出一些动作(通知或中断)

5.TIME_WAITING 超时等待,不同于WAITING,是可以在指定时间自行返回的

6.TERMINATED 终止状态,表示该线程已经执行完毕

提示:线程被创建之后,调用start()方法开始运行。当线程执行wait()方法之后,线程进入等待状态。当线程调用同步方法时,在没有获取到锁的状态下,线程将会进入到阻塞状态。线程在执行Runnable的run()方法之后将会进入到终止状态。

注意:阻塞状态是线程阻塞在进入 synchronized 关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在 java.concurrent 包中的 Lock 接口的线程状态是等待状态,因为 java.concurrent 包中接口 Lock 对于阻塞的实现均使用了 LockSupport 类中的相关方法。

4.1.5 Daemon 线程

Daemon线程是一种支持型线程,因为它主要被用作程序中后台调用以及支持性工作。表示在一个Java虚拟机中 不存在非Daemon线程 的时候,Java虚拟机将会退出。

提示:Daemon线程被用作支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块并不一定会执行。

注意:在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。

4.2 启动或终止线程

4.2.1 构造线程

在运行线程之前首先要构造一个线程对象,线程对象在构造的时候需要提供线程所需要的属性,如:线程所属线程组、线程优先级、是否是Daemon线程等信息。

4.2.2 启动线程

线程对象在初始化完成之后,调用start()方法可以启动这个线程。start()方法表示:当前线程同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。

提示:启动一个线程之前设置线程名称,在利用jstack分析程序或者进行问题排查可以方便一些。

4.2.3 中断

4.2.4 过期的 suspend() | resume() | stop()

对线程做 暂停 恢复 停止 操作对应的API就是 suspend() | resume() | stop()。但是这些方法都是过期了的,是因为以suspend()方法为例,在调用后线程不会释放已经占有的资源(如锁),而是占用资源进入睡眠状态,容易引起死锁问题。

提示:因为 suspend() | resume() | stop() 带来的副作用,而暂停和恢复操作可以用后面提到的等待/通知机制来替代。

4.2.5 安全终止线程

通过标识位或者中断操作的方式能够使得线程在终止时有机会去清理资源,不是武断将线程停止,因此这种操作更加安全和优化。

4.3 线程间通信

4.3.1 volatile和synchronize关键字

关键字 volatile 可以用来修饰字段,就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。

提示:过多的使用volatile关键字会降低程序执行的效率。

关键字synchronize可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或同步块中。保证线程对变量访问的可见性和排它性。

4.3.2 等待/通知机制

等待/通知的相关方法是任意Java对象都具备的。因为这些方法被定义在所有对象的超类 java.lang.Object 上。

1.notify() 通知一个在对象上等待的线程,使其从 wait() 方法返回,而返回的前提是该线程获取到了对象的锁。

2.notifyAll() 通知所有等待在该对象上的线程。

3.wait() 调用该方法的线程进入 WAITING 状态

4.wait(long) 超时等待一段时间

通知/等待机制表示一个线程 A 调用该对象O的wait()方法进入等待状态,而另一个线程 B 调用了对象O的notify()或者notifyAll()方法,线程 A 收到通知后从对象O的wait()方法返回,进而执行后续操作。

4.3.3 等待/通知的经典范式

等待/通知的经典范式分为两部分:等待方(消费者)和通知方(生产者)。

  • 等待方遵循原则:

    1.获取对象的锁。

    2.如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。

    3.条件满足时执行对应的逻辑。

  • 通知方遵循原则:

    1.获取对象的锁。

    2.改变条件。

    3.通知所有等待在对象上的线程。

4.3.4 管道输入/输出流

管道输入/输出流和普通文件的输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输媒介是内存。

管道输入/输出流主要包括以下四种具体实现:

1.PipedOutputStream

2.PipedInputStream

3.PipedWriter

4.PipedReader

提示:对于Piped类型的流,必须先要进行绑定,也就是调用connect()方法,如果没有将输入/输出流绑定起来,对于该流的访问将会抛出异常。

4.3.5 Thread.join()

join() 表示当前线程A等待thread线程终止之后才从thread.join()返回。

4.3.6 ThreadLocal 的使用

ThreadLocal 线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,即一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。

4.4 线程应用实例

本文标题:并发编程 基础

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:18:13

原始链接:http://bangjinhu.github.io/undefined/第4章 Java并发编程基础/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.