Java并发编程的艺术-----第四章读书笔记
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)设置为守护线程。
Java虚拟机退出时,守护线程中的finally块不一定会执行。
因为系统中一旦没有非守护线称的时候,守护线程将会立即关闭。
4.2.3
中断可以理解为线程的一个标识位属性。P92
我一般将其理解为中断标志位。如果这个线程调用 Thread.isInterrupted()方法,返回的是false,说明就是未中断的。
方法在抛出InterruptedException之前,Java虚拟机会先将程序的中断标志位清除。P92
终于找到为什么每次进入异常处理语句后,此线程的中断标志位就变成false了。
在补充一句:哪个线程直接调用了 interrupt()方法,哪个线程的中断状态位立即置为true。而很多方法,比如Thread.sleep()方法,对中断状态位敏感,一旦侦测到中断状态位为true,立即中断线程,进入异常处理语句。
4.2.4
为什么 stop()、suspend()方法被废弃。因为这两个方法一个立即释放同步锁(导致脏读,资源没清理),一个不释放同步锁(导致死锁)。
4.2.5
如何安全的停止线程? 目标:终止线程的时候有机会去清理资源,而不是武断的停止线程。
方法一就是采用中断状态位了。调用interrupt()方法即可。任务循环判断 线程的中断状态位 isInterrupted()。
方法二就是为线程定义一个boolean类型的标记flag,提供一个方法改变这个flag的值。任务循环判断这个flag即可。
4.3.1
关键字synchronized表示:同一时刻,只能有一个线程处于临界区中。它能保证变量的可见性与排他性。P96
可见性分析:在释放锁的时候,将线程私有变量刷新到主存。获取锁的时候,将私有内存中变量的值废弃,重新从主存中获取最新变量的值。
排他性分析:在进入同步代码之前,使用监视器进入指令。在方法退出或者异常出,使用监视器退出指令。保证临界区内中有一个线程。
synchronized的原理可以这么理解:同一时刻,只能有一个线程能够获取到synchronized所保护的同步锁的监视器对象。
4.3.3
消费者生产者模式经典范式。
(1)获取对象的锁
(2)如果条件不满足,调用wait()方法,被通知后仍然需要检查条件。
(3)条件满足,执行业务逻辑。
synchronized (lock){
while(条件不满足){
lock.wait();
}
doSomething(); //执行业务逻辑;
lock.notifyAll();//唤醒其它线程
}
4.3.6
作者提供了一个非常使用的性能分析工具,采用ThreadLocal实现,可以应到到AOP中。我的想法是在前置通知中,调用开始记录时间的方法,将当前时间记录到ThreadLocal中。在后置返回通知或者后置异常通知里面调用另外一个方法,计算出本次方法的耗时。
参考:https://github.com/hairdryre/MyJavaLife/blob/master/src/main/java/cn/jay/aop/TimeReporter.java
4.4.1
等待超时经典代码
/**
* 在指定时间之内期望获取一个结果
* 若没有获取到结果,那么返回默认的结果
*
* 时间轴, remain = future - now;
*
* --------start----------now----------future
*
* @param millis 指定时间
* @return 期望得到的结果,若在时间之内获取失败,返回默认的结果
* @throws InterruptedException 中断异常
*/
public synchronized Object get(Long millis) throws InterruptedException {
//截止时间点
long future = System.currentTimeMillis() + millis;
//剩余时间
long remain = millis;
//当结果为空或者剩余时间还有的时候,执行下面的方法
while (result == null && remain > 0) {
//继续等待其它线程唤醒。
//这个方法时间到了以后,会自动唤醒
wait(remain);
//如果是其它线程叫醒的,看看剩余时间还剩多久
remain = future - System.currentTimeMillis();
}
return result;
}