final 使用
此关键字使用过程中需要注意的问题,引用不可变,值可变;如果是基本类型,如int
,则值也不可变
FinalChangeObject obj = new FinalChangeObject();
obj.setC(1);
final FinalChangeObject t = obj;
obj.setC(2);
System.out.println(t.getC());
FinalChangeObject obj2 = new FinalChangeObject();
// t = obj2; // 这里就会报错,t 已经初始化引用到obj了,不能再改变其引用
// 但是:对象的值是可以修改的,如上面的 obj.setC(2);
组合使线程安全
当为现有的类添加一个原子操作时,有一种更好的文法:组合(compostion)。通过自身的内置锁增加了一层额外的锁,它并不关心底层的list 是否是线程安全的,即使list 不是线程安全的或者修改了它的加锁实现,ImprovedList 也会提供一致的加锁机制来实现线程安全性。虽然额外的同步层可能导致轻微的性能损失,但与模拟另一个对象的加锁策略相比,ImporovedList 更为健壮。事实上,我们使用了java 监视器模式来封装现有的List,并且只要在类中拥有指向底层List 的唯一外部引用,就能确保线程安全性
6.2.1 基于Executor
的web 服务器示例
这是一段基于线程池的代码示例。通过使用Executor
将请求处理任务的提交与任务的实际处理解耦开来
6.2.2 执行策略
通过将任务的提交与执行解耦开来,从而无须太大的困难就可以为某种类型的任务指定和修改执行策略。在执行策略中定义了任务执行的What, Where, When, How
在实际编程中,每当看到下面这种形式的代码时
new Thread(runnable).start()
并且你希望获得一种更灵活的执行策略时,就可以考虑使用Executor
来代替Thread
6.2.4 Executor
生命周期
有几种停止的方法,使用时来查
6.2.5 延迟任务与周期任务
都使用newScheduledThreadPoolExecutor
就可以了,Timer已经过时,不可用
6.3.3 使用Future 实现页面渲染器
为了拿页面渲染器实现更就的并发性,首先将渲染过程分解为两个任务,一个是渲染所有的文本,另一个是下载所有的图像。(因为其中一个任务是CPU 密集型,而另一个是I/O密集型,因此这种方法即使在单CPU系统上也能提升性能)
第六章总结
通过围绕任务执行来设计应用程序,可以简化开发过程,并有助于实现并发,
Executor
框架将任务提交与执行策略解耦开来,同时还支持多种不同类型的执行策略。当需要创建线程来执行任务时,可以考虑使用Executor
。要想在将应用程序分解为不同的任务时获得最大的好处,必须定义清晰的任务边界。某些应用程序中存在着比较明示的任务边界,而在其他一些程序中,则需要进一步分析才能提示出粒度理细的并行性。
如某个任务等待另一个任务的返回值或执行结果,那么除非线程池足够大,否则将发生线程饥饿死锁
第七章 取消与关闭
7.1 任务取消
在Java 中没有 一种安全的抢占式方法来停止线程,因此也就没有安全的抢占式方法来停止任务。只有一些协作式的机制,使请求取消的任务和代码都遵循一种协商好的协议。 其中一种协作机制能设置某个”已请求取消“标志,而任务将定期地查看该标志,如果设置了这个标志,那么任务将提前结束。
7.1.1 中断
阻塞库方法如,Thread.sleep
和 Object.wait
,都会检查线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态,抛出InterruptedException
,表示阻塞操作由于中断而提前结束。JVM并不能保证阻塞方法检测到中断的速度,但在实际情况中响应速度还是非常快的。
总结
在任务、线程、服务以及应用程序等模块中的生命周期结束问题,可能会增加它们在设计和实现时的复杂性。Java 并没有提供某种抢占式的机制来取消操作或者终结线程。相反,它提供了一种协作式的中断机制来实现取消操作,但这要依赖于如何构建取消操作的协议,以及能否始终遵循这些截。通过使用
FutureTask
和Executor
框架,可以帮助我们构建可取消的任务和服务