线程的优势
如果使用得当,线程可以有效地降低程序的开发、维护的成本,同时提升复杂应用程序的性能。线程可以降低代码的复杂度,使代码更容易编写、阅读和维护。在GUI应用程序中,线程可以提高用户界面的响应灵敏度,在服务器应用程序中,可以提升资源利用率以及系统吞吐率。
线程带来的风险
线程安全性是非常复杂的,在没有充分同步的情况下,多个线程中的操作顺序是不可预测的,甚至会产生奇怪的结果。
安全性问题
下面是一个非线程安全的数值序列生成器。在单线程环境下,这个类能正确工作,但在多线程环境下则不能。线程安全性是不可破坏的,安全性不仅对多线程程序很重要,对于单线程程序同样重要。
@NotThreadSafe
public class UnsafeSequence {
private int value;
public int getNextVal(){
return value++;
}
}
问题在于,如果执行时机不对,两个线程调用getNextVal()
会得到相同的值。这是一种常见的并发安全问题,称为竞态条件(Race Condition)。在多线程环境下,getNextVal()是否会返回唯一的值,要取决于运行时对线程中操作的交替执行方式。
把getNextVal()修改为同步方法,可以修复错误。
@ThreadSafe
public class Sequence {
private int value;
public synchronized int getNextVal() {
return value++;
}
}
活跃性问题
活跃性问题关注于“某件正确的事情最终会发生”这个目标。活跃性问题包括死锁、饥饿,以及活锁。
性能问题
活跃性意味着某件正确的事情最终会发生,但却不够好,因为我们总是希望它尽快发生。性能问题包括:服务时间过长、响应不灵敏、吞吐率过低、资源消耗过高或者可伸缩型较低等等。
在设计良好的并发应用程序中,线程能提升性能,但是无论如何,线程总会带来运行时的开销。当线程调度器临时挂起活跃线程并转而运行另一个线程时,就会频繁地进行上下文切换操作(Context Switch),CPU将花更多时间在线程调度而不是线程运行上。当线程共享数据时,必须使用同步机制,这些机制往往会一直某些编译器优化,使内存缓冲区中的数据无效,以及增加共享内存总线的同步流量。所有的这些因素都将带来额外的性能开销。