JAVA并发1:并发编程的挑战

534 阅读2分钟

上下文切换

一个CPU同一时刻只能执行一个线程;CPU 通过给每个线程分配一定的时间片,时间片非常短,通常是几十毫秒,来不停的切换线程执行任务,达到了多线程的效果。当线程用完自己的时间片后,及时任务还没有完成,操作系统也会剥夺它的执行权,让另一条线程执行。

当一条线程的时间片用完后,操作系统会暂停该线程,并保存该线程相应的信息(到程序计数器),然后再随机选择一条新线程去执行,这个过程称为“上下文切换”。由于进行上下文切换时,需要保存当前线程的状态,并加载线程先前的状态,这是有消耗的,因此应该尽量减少上下文切换的次数。

减少上下文切换

  • 减少线程的数量,避免创建不必要的线程。
  • 采用无锁并发编程。采用一些办法避免使用锁,如将数据ID按照 Hash(id) 进行取模分段,每个线程处理各自分段的数据,避免使用锁。
  • 采用 CAS 算法。 Java 的 Atomic 包使用 CAS 算法更新数据,不需要加锁。
  • 协程。在单线程里实现多任务调度,并在单线程里维持多个任务间的切换。

死锁

当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁,那么它们将被永远阻塞。在线程A持有锁L1并想获得锁L2的同时,线程B持有锁L2并想获得L1,那么这两个线程将永远等待下去,也就是产生了死锁。

避免死锁的几个常见方法:

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个计算机资源,尽量保证一个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁都要保证必须在一个数据库连接里,否则会出现解锁失败的情况。

资源限制

资源限制是指在进行并发编程时,程序的执行速度受限于计算机的硬件资源或者软件资源,这些计算机资源限制了程序的并发度。

线程多了导致线程的上下文切换增多,资源有限,会产生损耗。

解决资源限制的方法:

  • 对于硬件资源限制,考虑使用集群并行程序。
  • 对于软件资源限制,可以考虑使用资源池对资源进行复用,如线程池和数据库连接池。

参考资料