《Java并发编程的艺术》读书笔记
线程基本概念
Java中通过继承Thread类,并重写run方法,然后调用start方法开启线程,或者通过向Thread构造函数中传入实现Runnable的类的实例然后调用start方法来开启线程。
线程创建后可以设置其是否为守护线程,守护线程在所有的非守护线程退出后自动退出,该属性必须在启动线程之前设置。
线程可以通过其他线程调用该线程的interrupt方法来中断该线程,被中断的线程可以调用isInterrupted方法来测试,这个方法不会影响线程的中断标识。通过静态方法interrupted也可以测试当前线程的中断情况,不过这个方法会重置线程的中断标识。
方法suspend、resume、stop已标识过期,不推荐使用。停止线程可以通过在run方法中测试中断标识或者测试自定义的表示来实现。
在Java中的线程有六种状态:
状态名称 | 说明 |
---|---|
New | 初始状态,线程被构建,但是还没有调用start方法 |
Runnable | 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称为运行中 |
Block | 阻塞状态,表示线程阻塞于锁 |
Waiting | 等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
Time_Waiting | 超时等待,不同于Waiting,它是可以在指定时间自行返回 |
Terminated | 终止状态,表示当前线程已经执行完毕 |
线程间通信
线程间通信可以通过等待通知机制来实现。也就是通过Object类中的wait、notify和notifyAll方法来实现。这些方法一般都是在获得了该对象锁后调用的,调用了wait方法后,线程会阻塞在该对象上,在其他线程在该对象上调用notify或notifyAll方法后会唤醒阻塞在该对象上的线程,notify会唤醒阻塞队列中的第一个线程,而notifyAll将会唤醒阻塞在该对象上的所有线程。
thread.join方法会使当前线程等待thread线程执行完返回后再继续执行。
Java中的锁
Java中锁相关的类在java.util.concurrent.locks包下。
Lock接口定义了锁的主要API。
方法 | 描述 |
---|---|
void lock() | 获取锁,调用该方法的当前线程会获取锁,在获得锁后,从该方法返回 |
void lockInterruptibly() | 可中断地获取锁,和lock方法不同的地方是该方法会响应中断,在锁的获取中可以中断当前线程 |
boolean tryLock() | 尝试非阻塞获取锁,调用该方法后立即返回,如果能获取则返回true,否则返回false |
boolean tryLock(long time, TimeUnit unit) | 超时的获取锁 |
void unlock() | 释放锁 |
Condition newCondition() | 获取等待通知组件,该组件和当前的锁绑定,当前线程只有在获取锁后才能调用该组件的wait方法,调用后,当前线程将释放锁 |
重入锁
重入锁是指在获取锁后的代码块中再次获取锁也能成功而不会阻塞的锁。synchronized支持可重入,ReentrantLock类是重入锁的一个实现。通过构造函数可以创建一个公平或非公平的ReentrantLock类实例,公平锁会将锁给最早获取锁的线程。
可重入锁需要自己维护一个锁获取次数,同一个线程在每次获取锁的时候都要增加该次数,在每次释放锁的时候减少该次数,在减少到0时表示该线程释放了锁。
读写锁
读写锁是将读操作和写操作分别使用不同的锁来进行同步的锁,分读锁和写锁,读锁是该共享锁,写锁是独占锁。这样的设计能提高多线程读的操作,特别适合读操作多而写操作少的情况。
ReentrantReadWriteLock是一个读写锁的实现,它同时也是该重入锁,它使用一个整型变量来维护读写状态,变量高16位维护读状态,低16位维护写状态。
Condition
Object类的wait、notify和notifyAll方法和synchronized一起来实现等待通知机制,而Condition类和Lock也可以实现这个,而且还比前者更灵活,Condition必须通过Lock的newCondition方法来获得。
方法 | 描述 |
---|---|
await() | 当前线程进入等待状态直到被通知(signal)或中断 |
awaitUninterruptibly() | 不可中断的等待 |
awaitNanos(long timeout) | 超时等待。 |
awaitUtil(Date deadline) | 当前线程进入等待状态直到被通知、中毒或到某个时间点 |
signal() | 唤醒一个在Condition上等待的线程 |
signalAll() | 唤醒所有在Condition上等待的线程 |
队列同步器(AbstractQueuedSynchronizer)
队列同步器是用来构建锁或者其他同步组件的基础框架,它使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
同步器主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,同步器提供了3该方法(getState()、setState(int newState)和compareAndSetState(int expect, int update))来进行操作,它们能够保证安全的改变状态。
子类推荐被定义为自定义同步组件的静态内部类,同步器可以支持独占式的获取同步状态,也可以支持共享式的获取同步状态。
同步器可重写方法:
方法 | 描述 |
---|---|
boolean tryAcquire(int arg) | 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态 |
boolean tryRelease(int arg) | 独占式释放同步状态 |
int tryAcquireShared(int arg) | 共享式获取同步状态,返回大于0的值,表示获取成功 |
boolean tryReleaseShared(int arg) | 共享式释放同步状态 |
boolean isHeldExclusively() | 当前线程是否在独占式模式下被线程占用,一般表示是否被当前线程锁独占 |
同步器提供的模板方法:
方法 | 描述 |
---|---|
void acquire(int arg) | 独占式获取同步状态 |
void acquireInterruptibly(int arg) | 与acquire相同,但是能响应中断 |
boolean tryAcquireNanos(int arg, long nanos) | 在acquireInterruptibly基础上加了超时限制 |
void acquireShared(int arg) | 共享式获取同步状态 |
void acquireSharedInterruptibly(int arg) | 与acquireShared,但能响应中断 |
boolean tryAcquireSharedNanos(int arg, long nanos) | 在acquireSharedInterruptibly基础上加了超时限制 |
boolean release(int arg) | 独占式释放同步状态 |
boolean releaseShared(int arg) | 共享式释放同步状态 |
Collection |
获取等待在同步队列上的线程集合 |
示例代码:
1 | import java.util.concurrent.TimeUnit; |
同步器依赖内部的同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点通过CAS加入到队列中,同时阻塞当前线程,当同步状态释放时,会把首节点的线程唤醒使其再次尝试获取同步状态。