4.1.2
为什么要使用多线程?
一个线程在同一时刻只能运行在一个处理器核心上。如果程序使用多线程,那么程序就会被分配到多个处理器核心上,这样就会显著减少程序的处理时间,并且随着更多处理器核心的加入,程序的执行会变得更有效率。P84
耗时少,效率高。
4.1.3
线程会得到OS分配的若干时间片,当线程的时间片用完了就会发生线程调度,并等待着下次OS分配。
4.1.4
如何查看线程的状态?
jps ,我们运行的Java类的名字是ThreadState。所以JPS可以看到我的Java代码的进程ID为3068,
jstack 3068 查看进程的中的所有线程信息。
如下图可以发现,线程名为"BolckedThreadB"的线程的状态为 TIMED_WAITING ,说明此线程调用了 Thread.sleep()方法。
下图也显示了,名为BlockedThreadA的线程的状态,现在是BLOCKED状态。BolckedThreadA这个线程与BolckedThreadB这个线程共享的是同一把锁,而BolckedThreadB线程调用了Thread.sleep()方法不会释放同步锁。所以这个BolckedThreadA线程就没能够获取到同步锁,所以只能进入同步队列中,进入阻塞状态 BLOCKED。
4.1.5
join()方法内部使用的wait()方法,因此,调用A.join()方法的当前线程,注意是当前线程,将会进入阻塞状态WAITING,并同时释放同步锁。当前线程等待线程A执行完毕后继续执行。
线程对象调用 Thread.setDaemon(true)设置为守护线程。
守护线程中的finally块不一定会执行。
一旦没有非守护线称的时候,守护线程将会立即关闭。
4.2.3
中断可以理解为线程的一个标识位属性。P92
中断标志位。如果这个线程调用 Thread.isInterrupted()方法,返回的是false,说明就是未中断的。
抛出InterruptedException之前,Java虚拟机会先将程序的中断标志位清除。P92
终于找到为什么每次进入异常处理语句后,此线程的中断标志位就变成false了。
在补充一句:哪个线程直接调用了 interrupt()方法,哪个线程的中断状态位立即置为true。而很多方法,比如Thread.sleep()方法,对中断状态位敏感,一旦侦测到中断状态位为true,立即中断线程,进入异常处理语句。
4.2.4
立即释放同步锁(导致脏读,资源没清理),一个不释放同步锁(导致死锁)。
4.2.5
停止线程? 目标:终止线程的时候有机会去清理资源,而不是武断的停止线程。
采用中断状态位了。调用interrupt()方法即可。任务循环判断 线程的中断状态位 isInterrupted()。
定义一个boolean类型的标记flag,提供一个方法改变这个flag的值。任务循环判断这个flag即可。
4.3.1
同一时刻,只能有一个线程处于临界区中。它能保证变量的可见性与排他性。P96
可见性分析:在释放锁的时候,将线程私有变量刷新到主存。获取锁的时候,将私有内存中变量的值废弃,重新从主存中获取最新变量的值。
排他性分析:在进入同步代码之前,使用监视器进入指令。在方法退出或者异常出,使用监视器退出指令。保证临界区内中有一个线程。
synchronized的原理可以这么理解:同一时刻,只能有一个线程能够获取到synchronized所保护的同步锁的监视器对象。
4.3.3
消费者生产者模式经典范式。
(1)获取对象的锁
(2)如果条件不满足,调用wait()方法,被通知后仍然需要检查条件。
(3)条件满足,执行业务逻辑。
4.3.6
作者提供了一个非常使用的性能分析工具,采用ThreadLocal实现,可以应到到AOP中。我的想法是在前置通知中,调用开始记录时间的方法,将当前时间记录到ThreadLocal中。在后置返回通知或者后置异常通知里面调用另外一个方法,计算出本次方法的耗时。
参考:https://github.com/hairdryre/MyJavaLife/blob/master/src/main/java/cn/jay/aop/TimeReporter.java
4.4.1
等待超时经典代码