线程
-
(P 552)多进程和多线程的本质区别:每一个进程都拥有自己的一整套变量,而线程共享数据
-
(P 555)线程具有6种状态:
- New(新建):使用
new
操作符创建线程时 - Runnable(可运行):调用
start
方法 - Blocked(阻塞)
- Waiting(等待)
- Timed waiting(计时等待)
- Terminated(终止):
run
方法正常退出、没有捕获异常
- New(新建):使用
-
(P 558)
interrupt
方法用来请求终止一个线程。当对一个线程调用interrupt
方法时,就会设置线程的中断状态。每个线程都应该不时地检查这个标志,以判断线程是否被中断 -
(P 559)如果线程被阻塞,就无法检查中断状态,因此引入
InterruptedException
异常。当在一个被sleep
或wait
调用阻塞的线程上调用interrupt
方法时,那个阻塞调用将被一个InterrupedtException
异常中断 -
(P 559)如果设置了中断状态,此时倘若调用
sleep
方法,它不会休眠。实际上,它会清除中断状态并抛出InterruptedException
。因此,如果循环里调用了sleep
,不要检测中断状态,而应当捕获InterruptedException
异常 -
(P 560)
interrupted
、isInterrupted
以及interrupt
方法的区别:方法 性质 作用 影响 interrupted
Thread
的静态方法检查当前线程是否被中断 会清除该线程的中断状态 isInterrupted
Thread
的实例方法测试线程是否被中断 不会改变中断状态 interrupt
Thread
的实例方法向线程发送中断请求 会设置线程的中断状态 -
(P 561)守护线程:唯一用途是为其他线程提供服务,当只剩下守护线程时,虚拟机就会退出。可通过
setDaemon
方法将线程设为守护线程 -
(P 561)线程的
run
方法不能抛出任何检查型异常,在线程死亡之前,异常会传递到一个用于处理未捕获异常的处理器(必须实现Thread.UncaughtExceptionHandler
接口)方法 性质 作用 setUncaughtExceptionHandler
Thread
的实例方法为任何线程安装一个处理器 setDefaultUncaughtExceptionHandler
Thread
的静态方法为所有线程安装一个默认的处理器
同步
-
(P 568)Java提供的两种可防止并发访问代码块的机制:
-
synchronized
关键字 -
ReentrantLock
类(重入锁)myLock.lock(); // 一个ReentrantLock对象 try { ... } finally { myLock.unlock(); // 必须放在finally里,不能使用try-with-resources }
-
-
(P 570)重入(reentrant)锁:线程可以反复获得已拥有的锁,被一个锁保护的代码可以调用另一个使用相同锁的方法。注意确保临界区中的代码不要因为抛出异常而跳出临界区
-
(P 572)一个锁对象可以有一个或多个相关联的条件对象,可以使用
newCondition
方法获得一个条件对象。方法 性质 作用 newCondition
ReentrantLock
的实例方法获得一个条件对象 await
Condition
的实例方法当前线程现在暂停,并放弃锁 signalAll
Condition
的实例方法解除等待这个条件的所有线程的阻塞状态 signal
Condition
的实例方法随机选择一个线程解除其阻塞状态 使用形式:
class A { private var lock = new ReentrantLock(); private Condition condition; ... private A() { ... condition = lock.newCondition(); } private someMethod() { lock.lock(); try { ... while(!(OK to proceed)) { // await调用通常放在循环中 condition.await(); } ... condition.signalAll(); // signalAll只是通知等待的线程:现在有可能满足条件,值得再次检查条件 // 只要一个对象的状态有变化,而且可能有利于等待的线程,就可以调用signalAll // signalAll只是解除等待线程的阻塞,使这些线程可以在当前线程释放锁之后竞争访问对象 } finally { lock.unlock(); } } ... }
-
(P 576)Java中的每个对象都有一个内部锁(只有一个关联条件)。如果一个方法声明时有
synchronized
关键字,那么对象的锁将保护整个方法方法 性质 作用 等价于 wait
Object
的实例方法将一个线程增加到等待集中 await
notify
/notifyAll
Object
的实例方法解除等待线程的阻塞 signal
/signalAll
-
(P 577)将静态方法声明为同步也是合法的,如果调用这样一个方法,它会获得相关类对象(
Class
对象)的内部锁 -
(P 577)内部锁和条件存在一些限制:
- 不能中断一个正在尝试获得锁的线程
- 不能指定尝试获得锁时的超时时间
- 每个锁仅有一个条件可能是不够的
-
(P 579)同步块:
synchronized(obj) { // 会获得obj对象的锁 ... }
-
(P 580)监视器的特性:
- 监视器是只包含私有字段的类
- 监视器类的每个对象有一个关联的锁
- 所有方法有这个锁锁定
- 锁可以有任意多个相关联的条件
-
(P 581)
volatile
关键字为实例字段的同步访问提供了一种免锁机制,volatile
变量不能提供原子性- 另一种安全访问共享字段的情况:将字段声明为
final
- 另一种安全访问共享字段的情况:将字段声明为
-
(P 582)
java.util.concurrent.atomic
包中有很多类使用了很高效的机器级指令来保证其他操作的原子性 -
(P 586)线程局部变量:
ThreadLocal
public static final ThreadLocal<SimpleDateFormat> dataFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); // 在一个给定线程中首次调用get时,会调用构造器中的lambda表达式 // 在此之后,get方法会返回属于当前线程的那个实例 String dateStamp = dataFormat.get().format(new Date());
线程安全的集合
-
(P 589)阻塞队列(blocking queue):
add
、element
、offer
、peek
、poll
、put
、remove
、take
-
(P 595)高效的映射、集和队列:
java.util.concurrent
包提供了ConcurrentHashMap
、ConcurrentSkipListMap
、ConcurrentSkipListSet
、ConcurrentLinkedQueue
-
(P 602)同步包装器(synchronization wrapper):任何集合类都可以通过使用同步包装器变成线程安全的
List<E> synchArrayList = Collections.synchronizedList(new ArrayList<E>()); Map<K, V> synchHashMap = Collections.synchronizedMap(new HashMap<K, V>());
线程池
-
(P 603)
Callable
与Runnable
类似,但是有返回值,只有一个call
方法 -
(P 604)
Future
保存异步计算的结果 -
(P 604)执行
Callable
的一种方法是使用FutureTask
,它实现了Future
和Runnable
接口Callable<Integer> task = ...; var futureTask = new FutureTask<Integer>(task); var t = new Thread(futureTask); // it's a Runnable t.start(); ... Integer result = futureTask.get(); // it's a Future
-
(P 605)执行器(
Executors
)类有许多静态工厂方法,用来构造线程池 -
(P 606)使用线程池时所做的工作:
- 调用
Executors
类的静态方法newCachedThreadPool
或newFixedThreadPool
- 调用
submit
提交Runnable
或Callable
对象 - 保存好返回的
Future
对象,以便得到结果或者取消任务 - 当不想再提交任何任务时,调用
shutdown
- 调用
-
(P 607)控制任务组
方法 性质 作用 备注 invokeAny
ExecutorService
的实例方法提交一个 Callable
对象集合中的所有对象,并返回某个已完成任务的结果invokeAll
ExecutorService
的实例方法提交一个 Callable
对象集合中的所有对象,并返回表示所有任务答案的一个Future
对象列表这个方法会阻塞,直到所有任务都完成 -
(P 612)fork-join框架:专门用来支持计算密集型任务,假设有一个处理任务,它可以很自然地分解为子任务
异步计算
- (P 615)
CompletableFuture
类实现了Future
接口,它提供了获得结果的另一种机制。你要注册一个回调,一旦结果可用,就会(在某个线程中)利用该结果调用这个回调(与之不同的是,Future
中的get
方法会阻塞) - (P 615)
Supplier<T>
与Callable<T>
:都描述了无参数而且返回值类型为T
的函数,不过Supplier
函数不能抛出检查型异常
进程
- (P 628)
Process
类在一个单独的操作系统进程中执行一个命令,允许我们与标准输入、输出和错误流交互。ProcessBuilder
类则允许我们配置Process
对象 - (P 631)
ProcessHandle
接口:要获得程序启动的一个进程的更多信息,或者想更多地了解你的计算机上正在运行的任何其他进程,可以使用ProcessHandle
接口 - (P 631)得到
ProcessHandle
的4种方式:- 给定一个
Process
对象p
,p.toHandle()
会生成它的ProcessHandle
- 给定一个
long
类型的操作系统进程ID
,ProcessHandle.of(id)
可以生成这个进程的句柄 Process.current()
是运行这个java虚拟机的进程句柄ProcessHandle.allProcesses()
可以生成对当前进程可见的所有操作系统进程的Stream<ProcessHandle>
- 给定一个