并发编程 | 多线程的挑战

299 阅读2分钟
  • 上下文切换

    CPU核心(单核CPU或多个CPU的一个核心)通过给每个线程分配CPU时间片,让各线程有执行的机会。时间片非常短,通常为几十毫秒,所以CPU通过不停的切换线程,让人感觉多个线程是同时执行的。

    CPU核心在当前线程(A线程)的时间片执行完之后,会切换到下一个线程(B线程)。在切换之前,会保存A线程的状态,以便切回A线程时可以回到上一次的状态。从状态保存到再次加载的过程,就是一次上下文切换。

    需要注意的是,上下文切换是开销较大的,过多的上下文切换,会影响多线程的执行速度。所以在开发中可以通过一些方法,尽量减少上下文切换。如:

    • 无锁并发编程:volatile关键字、cas算法(Java的Atomic包使用CAS算法更新数据,而不需要加锁)等
    • 使用最少线程:线程池的运用等
    • 使用协程:Java中没有引入这个概念,可以学习Golang中的协程
  • 死锁

    线程A持有a锁,线程B持有b锁,线程A想要获取b锁,线程B又想获取a锁,两人相互等待对方释放锁,反而引起死锁。

    避免死锁的常见方法:

    • 避免一个线程同时获取多个锁
    • 避免一个线程在锁内同时占用多个资源,尽量每个锁只占用一个资源
    • 使用定时锁,在一定时间内无法获取即放弃
    • 锁必须要及时释放
  • 资源限制

    并发编程时,程序执行速度受限于硬件(如带宽大小、硬盘IO速度)、其他软件资源(数据库连接数量)。

    常见的措施有:

    • 使用集群方案,应对硬件资源限制
    • 使用连接池、线程池等方式,资源复用,应对软件资源限制
    • 根据资源情况,调整适合的并发度,减少上下文切换、资源调度