《Java并发编程的艺术》读书笔记 第四章 Java并发编程基础
文章目录
1.线程介绍
1.1 为什么使用多线程
- 更多的处理器核心
- 更快的响应时间
- 更好的编程模型
1.2 线程优先级
通过整型成员变量priority控制优先级,范围从1-10,默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程。线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不理会Java线程对于优先级的设定。
1.3 线程的状态
一共有6种不同的状态,某一时刻,只能处在其中的一个状态。
Java将操作系统中的运行和就绪两个状态合并为运行状态,阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块时的状态,但是阻塞在java.concurrent包中Lock接口的线程状态是等待状态,因为java.concurrent包中Lock接口对于阻塞的实现使用了LockSupport类中的相关方法。
1.4 Daemon线程
Daemon线程是一种支持性线程,因为主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机不存在非Daemon线程的时候,Java虚拟机会退出。只能在启动线程之前设置Daemon属性!
在构建Daemon线程的时候,不能依靠finally块中的内容确保执行关闭或清理资源的逻辑,因为可能finally块中的代码不会被执行。
2.启动和终止线程
构造线程不必多说,一共有3种方式。线程对象在初始化完成以后,调用start方法就可以启动该线程。start方法的含义是当前线程同步告知Java虚拟机,只要线程规划器空闲,应该立即启动调用start方法的线程。
2.1 关于中断
线程通过方法isInterrupted()
来进行判断是否被中断,也可以用静态方法Thread.interrupted()
对当前线程的中断标识位进行复位。如果该线程已经终结,就算是该线程被中断过,调用该线程对象的isInterrupted()
方法也会返回false。
有一些方法会涉及InterruptException异常,这些方法在抛出异常之前,虚拟机会先将该线程的中断标识位清除,然后在抛出异常,调用isInterrupted()
方法也是返回false。
2.2 过期的suspend()、resume()、stop()
不建议使用的原因是:以suspend()方法为例,在调用后,线程不会释放已经占有的资源(如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样的,stop()方法在终结一个线程的时候不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。
3.线程间通信
3.1 volatile和synchronized关键字
volatile可以修饰字段,告知程序任何对该变量的访问都需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证线程对变量访问的可见性。
synchronized可以修饰方法或者以同步块的形式来进行使用,主要确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。执行方法的线程必须先获取到该对象的监视器(monitor)才能进入同步块或者方法,而没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。
3.2 等待/通知机制
该机制是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()/notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互。
使用注意
- 使用notify()/notifyAll()/wait()方法的时候,需要先对调用对象加锁
- 调用wait()方法后,线程状态有RUNNING变成WAITING,并将当前线程放置到对象的等待队列。
- notify()或者notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或者notifyAll()的线程释放锁之后,等待线程才有机会返回。
- notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,被移动的线程由WAITING变成BLOCKED。
- 从wait方法返回的前提是获得了调用对象的锁。
3.3 等待/通知的经典范式
等待方遵循如下原则:
- 获取对象的锁
- 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件
- 条件满足则执行对应的逻辑
synchronized(obj){
while(!conditon){
obj.wait();
}
do something...
}
通知方遵循下面的原则:
- 获取对象的锁
- 改变条件
- 通知锁用等待在对象上的线程
synchronized(obj){
changeCondition();
obj.notify();
}
3.4 管道输入/输出流
管道输出/输入主要包括下面4种具体实现:PipedOutputStream、PipedInputStream、PipedReader、PipedWriter,前面2种面向字节,后面2种面向字符。
3.5 Thread.join()的使用
如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才从thread.join()返回,线程Thread除了提供join()方法之外,还提供了2个具备超时特性的方法。每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从join()方法返回,这里涉及了等待/通知机制。
3.6 ThreadLocal的使用
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。