Java并发基础(十)——线程优势及风险

本文探讨了线程在软件开发中的优势与潜在风险。优势包括提高程序性能、简化异步事件处理及增强用户界面响应性;风险则涉及安全性、活跃性及性能问题,如数据竞争、死锁和上下文切换开销。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线程的优势

        线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。线程能够将大部分的异步工作流转换成串行工作流,因此能更好地模拟人类的工作方式和交互方式。此外,线程还可以降低代码的复杂度,使代码更容易编写、阅读和维护。

        在GUI应用程度中,线程可以提高用户界面的响应灵敏度;在服务器应用程序中,可以提升资源利用率以及系统吞吐率。线程还可以简化JVM的实现,垃圾收集器通常在一个或多个专门的线程中运行。在许多重要的Java应用程序中,都在一定程度上用到了线程。

1,1 发挥多处理器的强大能力

         单线程的程序只能使用多处理器系统的部分资源,多线程程序可以同时在多个处理器上执行。倘若设计无误,那么多线程程序可以通过提高处理器资源的利用率来提升吞吐率。

 

1,2 建模的简单性

         若你需要去处理只是一种类型的事件时,那么在时间管理等方面通常会比较简单。而当你要去处理多种类型的事情时,那通常需要去对比不同类型的事情的重要性、紧急优先程度、时间管理等,也会需要在不同类型的事情之间切换,这将带来额外的开销。

 

1,3 异步事件的简化处理

        服务器应用程序在接受来自多个客户端的套接字连接请求时,若为每个客户端连接都分配 其各自的线程,并且使用同步I/O,如此一来就会降低此类程序的开发难度。

        如果程序对套接字执行读操作,但此时请求的数据还未响应,那这个读操作将一直阻塞,直到请求的数据传递过来。在单线程应用程序中,这不仅意味着在处理请求的过程中有停顿,且还意味着这个线程在被阻塞期间,对所有请求的处理都将停顿。为了避免这样的问题,单线程应用程序须使用非阻塞I/O,而非阻塞I/O的复杂性要远远高于同步I/O,并且很容易出错。假如每个请求都拥有自己的处理线程,那在处理某个请求时就算发生了阻塞,也不会影响其它请求的处理。

 

二、线程带来的风险

       多线程并发可以算是一个比较“高深”的内容,稍有不慎,会有不少风险。

2,1 安全性问题

       线程的安全在有时候是比较复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会有奇怪的结果。比如递增计数器, 在单线程程序中是OK的,但在多线程情况下就失效了。

@NotThreadSafe
public class UnsafeThread {

    private int count;


    /**
     * @Description 递增并返回
     * @Author Ethan
     * @Param []
     * @return int
     * @change: time:   author:  description:
     */
    public int getNext() {
        return count ++;
    }

}

       上述代码中,getNext()方法中,count的递增很简单,但它却不是三个独立的操作:先读取count值;然后将count的值加1,最后把计算结果写入count(涉及原子性操作和复合性操作)。在多线程情况下,不同线程之间的操作交替执行,因此两个线程可能同时执行了读操作,那这两个线程中拿到的count值就是一样的。然后都进行加1操作,最终在不同线程中操作结果却可能是一样的。

 

2,2 活跃性问题

        安全性不仅对多线程程序很重要,对于单线程程序也同样重要。活跃性关注的一个目标是——某件正确的事情最终会发生。那么,当某个操作无法继续执行下去的时候,就会发生活跃性问题。

        在串行程序中,活跃性问题的形式之一就是无意中造成的无限循环,从而使循环之后的代码无法执行。还有其它一些活跃性的问题,如死锁、饥饿、活锁等。

 

2,3 性能问题

        与活跃性问题密切相关的是性能问题。活跃性意味着某件正确的事情最终会发生,但却不够好。我们希望正确的事情尽快发生。性能问题包括多个方面:服务时间过长、响应不灵敏、吞吐率过低、资源消耗过高、可伸缩性较低等。与安全性和活跃性一样,在多线程程序中不仅存在与单线程程序相同的性能问题,还存在由于使用线程而引发的其它性能问题。

        在设计良好的并发应用程序中,线程能提升程序的性能,但无论如何,线程总会带来某种程序的运行时开销。在多线程程序中,当线程调度器临时挂起活跃线程并转而运行另一个线程时,就会频繁地出现上下文的切换操作(Context Switch),这种操作将带来极大 的开销:保存和恢复执行上下文,丢失局部性,并且CPU时间将更多的花在线程调度而不是线程运行上。

         当线程共享数据时,必须使用同步机制,而这些机制往往会抑制某些编译器优化,使内存缓存区中的数据无效,以及增加共享内存总线的同步流量。所有这些因素都将带来额外的性能开销。

 

注:以上内容参考《Java并发编程实战》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值