然而,线程所管理的context信息比进程要来得少,一般而言线程的context-switch操作要比进程快得多。
第8章 Worker Thread - 等到工作来,来了就工作
invocation与execution分离的用处:
1) 提高响应性:如果invocation与execution无法分离,当execution很花时间时,invocation的操作将被牵连。如果将invocation与execution事先分离,即使execution花时间,invocation也可以继续自己前进。这样能提高程序的响应性。
2) 控制实行顺序:如果invocation与execution无法分离,一旦invoke出来,就必须直接把它execute完。但,如果invocation与execution分离,execute的顺序就可以于invoke的次序无关。也就是说,我们可以对Request设立优先级,控制Channel传递Request给Worker的顺序。
3) 可取消和可重复执行:若能分离invocation与execution,就有办法做到“虽然invocation了,但将execution取消”的功能。同样,如果把Request保存下来,就可以做到重复execute。
4) 分散处理的第一步:因为invocation与execution分离了,所以invoke与execute的操作也容易拆开在两台计算机上执行。相当于Request的对象,可通过网络传送到另一台计算机。
Runnable对象,可以作为方法的自变量传递、堆到队列里、通过网络传递、甚至存进文件中。而这样的一个Runnable对象可以经过多次传来传去,最后传到某台计算机的某条线程上,才真正交付执行。
这时,Runnable接口就可以看作是GoF的Command Pattern中的Command。
当Swing组建一旦被实现,可能改变组件状态的程序代码、依赖于状态的程序代码,都必须交给Event-dispatching thread执行。
第9章 Future - 先给您这张提货单
第10章 Two-Phase Termination - 块把玩具收拾好,去睡觉吧
等待指定的线程结束时,要使用join()方法。另外,检查指定的线程现在是否结束了,可以使用java.lang.Thread的isAlive()方法。若返回值是true,该线程还活着,反之表示线程已经结束了。
java.lang.Runtime的实例方法addShutdownHook()会在Java执行环境全部结束时(调用System.exit()方法或所有非Daemon线程都结束时),调用指定Thread的start()方法(这时的Thread称为shutdown hook)。使用这个方法,可以编写整个程序的终止处理。
调用interrupt()方法后,可以中断掉线程。这里所说的中断掉线程,是指下面其中一种结果:
1) 线程变成“中断状态”对“状态”的反应。
2) 抛出“异常InterruptException”对“控制的反应”。
通常会是1)。只有线程在sleep、wait、join时会是2)(这个时候不会变成“中断状态”)。
然而,1)和2)是可以互相转换的。
中断状态-->InterruptedException异常的转换:
if (Thread.interrupted()) {
throw new InterruptedException();
}
在花时间的处理前,先加上这个if语句,可提高程序对中断的响应性,可以避免不知道自己已经被中断,还开始进行花时间的操作。
调用Thread.interrupted()方法后,当前线程就不是中断状态了,也就是说,只要调用一次Thread.interrpupted()方法后,中断状态就会被清除。
如果不想清除中断状态,而要检查当前线程是否被中断,要使用inInterrupted()实例方法,使用方式如下:
if (Thread.currentThread().inInterrupted()) {
// 若为中断状态时需要进行的处理(中断状态不会清除)
}
InterruptedException异常-->转换为中断状态:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
这样可以将收到的InterruptedException,转换为中断状态的形式。
InterruptedException异常-->转换为InterruptedException异常:
收到的InterruptedException异常,也可以不马上抛出去,而留下来晚点再抛:
InterruptedException savedException = null;
...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
savedException = e;
}
...
if (savedException != null) {
throw savedException;
}
第11章 Thread-Specific Storage - 每个线程的保管箱
Thread-Specific Storage Pattern是只有一个入口,但内部会对每个线程提供特有存储空间的Pattern。
如果使用Java标准链接库时,可以使用java.lang.ThreadLocal类加以实现。
java.lang.ThreadLocal的实例可以想像成一种集合结构或许会比较好理解。
ThreadLocal的set()方法,可以将参数所指定的实例,存放到调用set()的当前线程所对应的存储空间。
ThreadLocal的get()方法,可以将调用get()的当前线程所对应的存储空间中的对应的实例,取出返回(如果没有set过,则返回null)。
手边有一个假定单线程作为执行环境的对象。现在我们想将这个对象放在多线程环境下执行,又不想修改使用端的线程,也不能改变对象的接口。这时就使用Thread-Specfic Storage Pattern。
在此将目的对象当作TSObject,并建立与TSObject具有相同接口的TSObjectProxy,另外,为了管理“Client-->TSObject”的对照表,又加上TSObjectCollection。TSObjectProxy会使用TSObjectCollection取得当前线程所对应的TSObject,而将工作委托给这个TSObject。
第12章 Active Objects - 接受异步消息的主动对象
在多线程程序设计中,千万要随时意识到每个方法是由哪个线程调用的。
与平常不同的是,在这里的方法是由Client以外的线程在执行的。也就是说,Active Object Pattern做到了“异步的方法”。也可以说是做到了“异步的消息”。
再看看ActiveObject包的属性,可以看到,通过ActiveObject包中的所有的参与者的相互协调,组合成一个“主动对象”,这个主动对象,具有:
1) 具有接口(API)-ActiveObject interface定义了API。
2) 可以获取异步消息-Proxy参与者会将方法调用转换成MethodRequest对象,存放在ActivationQueue里。
3) 与Client调用不同的线程-Scheduler提供线程。
4) 可以执行处理-Servant可以单线程执行处理。
5) 可以返回返回值-以Future作为返回值的提货单。
请回想本章前面的比喻,许多人互相协调,可以构成一个“法人”。Active Object Pattern则是协调许多对象,构成一个“主动对象”。
Active Object Pattern是否适合使用要考虑问题的处理量的大小。处理量太小不适合使用的原因,是因为Proxy建立ConcreteMethodRequest与ActivationQueue进行沟通,这些工作的需要花费的时间也不容忽视。
我们可以把Active Object Pattern看作是在以单线程为前提的Servant外面包上一层皮,使多线程的Client可以使用它。
总结 - 多线程程序设计的模式语言
Single Threaded Execution Pattern - 能通过这座桥的,只有一个人
别名:Critical Section,Critical Region
背景:多个线程共享一个实例。
问题:若多个线程都擅自更改实例的状态,实例会丧失安全性。
解决方式:
首先,仔细找出实例状态不稳定的范围(临界区域)。并对临界区域加以防护,使同时执行的线程保持在只有一条的情况。这样一来,就能够保护住实例的安全性。
实现:Java语言里,可使用synchronized来实现出临界区间。
相关:
当实例的状态不会改变时,为了提升性能,可使用Immutable Pattern。
想要将引用实例状态的线程和改变实例状态的线程拆开,以提高性能使,可使用Read-Write Lock Pattern。
Immutable Pattern - 想破坏它也没办法
背景:多个线程共享一个实例,实例的状态不会改变。
问题:使用Single Threaded Execution Pattern,会降低性能。
解决方式:
当实例建立后状态就不会变化时,就要停止使用Single Threaded Execution Pattern。
为了避免失误造成更改了实例的状态,故将类写成无法由线程更改。另外,删除实例里所有用来更新状态的方法(setter)。引用实例状态用的方法(getter)就无妨。
使用Immutable Pattern可提高性能,但是要持续保证不变性(Immutability)并不简单,记得在帮助文件中也要注明这是一个immutable的类。
实现:在Java中使用private来隐藏字段,此外,由于无法确保不可更改,因此还要使用final。