P685)临界区
通过使用同步控制块,而不是对整个方法进行同步控制,可以使多个任务访问对象的时间性能得到显著提升。宁愿使用同步控制块而不是对整个方法进行同步控制的典型原因:使得其他线程能更多的访问(在安全的情况下尽可能多)。
P689)在其他对象上同步
synchronized块必须给定一个在其上进行同步的对象,并且最合理的方式是,使用其方法正在被调用的当前对象:synchronized(this)。在这种方式中,如果获得了synchronized块上的锁,那么该对象其他的synchronized方法和临界区就不能被调用了。因此,如果在this上同步,临界区的效果就会直接缩小在同步的范围内。
P690)线程本地存储
线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。ThreadLocal对象通常当作静态域存储。在创建ThreadLocal时,你只能通过get()和set()方法来访问该对象的内容,其中,get()方法将返回与其线程相关联的对象的副本,而set()会将参数插入到为其线程存储的对象中,并返回存储中的原有对象。
P693)ExecutorService.awaitTermination()等待每个任务结束,如果所有的任务在超时时间达到之前全部结束,则返回true,否则返回false,表示不是所有的任务都已经结束了。
P695)中断
Thread类包含interrupt()方法,因此你可以终止被阻塞的任务,这个方法将设置线程的中断状态。如果一个线程已经被阻塞,或者试图执行一个阻塞操作,那么设置这个线程的中断状态将抛出InterruptException。当抛出该异常或者该任务调用Thread.interrupted()时,中断状态将被复位。
为了调用interrupt(),你必须持有Thread对象。如果你在Executor上调用shutdownNow(),那么它将发送一个interrupt()调用给它启动的所有线程。然而,你有时也会希望只中断某个单一任务。如果使用ExecutorService,那么通过调用submit()而不是execute()来启动任务,就可以持有该任务的上下文。submit()将返回一个泛型Future<?>,其中有一个未修饰的参数,因为你永远都不会在其调用get()——持有这种Future的关键在于你可以在其上调用cancel(),并因此可以使用它来中断某个特定任务。如果你将true传递给cancel(),那么它就会拥有在该线程上调用interrupt()以停止这个线程的权限。
P697)Thread.sleep()方法是可以中断的,而I/O和在synchronized块上的等待是不可中断的。对于I/O阻塞问题,一个略显笨拙但是有时确实行之有效的解决方案,即关闭任务在其上发生阻塞的底层资源。nio类提供了更人性化的I/O中断,被阻塞的nio通道会自动的响应中断。ReentrantLock上阻塞的任务具备可以被中断的能力,这与在synchronized方法或临界区上阻塞的任务完全不同:Lock.lockInterruptibly()。
P701)检查中断
注意,当你在线程上调用interrupt()时,中断发生的唯一时刻是在任务要进入到阻塞操作中,或者已经在阻塞操作内部时。如果你要想总是可以通过调用interrupt()以停止某个任务,那么你可以通过检查中断来实现。你可以调用interrupted()来检查中断状态,这不仅可以告诉你interrupt()是否被调用过,而且还可以清除中断状态。清除中断状态可以确保并发结构不会就某个任务而中断这个问题通知你两次。你可以经由单一的InterruptedException或单一的成功的Thread.interrupted()测试来得到这种通知。如果想要再次检查以了解是否被中断,则可以在调用Thread.interrupted()时将结果存储起来。
P703)wait()与notifyAll()
有两种形式的wait()。第一种版本接受毫秒数作为参数,含义与sleep()方法里的参数的意思相同,都是值“在此期间暂停”。但是与sleep()不同的是,对于wait()而言:
- 在wait()期间对象锁是释放的。
- 可以通过notify()、notifyAll(),或者令时间到期,从wait()中恢复执行。
第二种,也是更常用形式的wait()不接受任何参数。这种wait()将无限等待下去,直到线程接收到notify()或者notifyAll()消息。
实际上,只能在同步控制方法或者同步控制块里调用wait()、notify()和notifyAll()。为了使该任务从wait()中唤醒,它必须首先重新获得当它进入wait()时释放的锁。在这个锁变得可用之前,这个任务是不会被唤醒的。