天天看点

第1章 并发编程的挑战

第1章 并发编程的挑战

1.1 上下文切换

即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。

  • CPU时间片:CPU分配给各个线程的时间,时间片非常短,所以我们感受不到CPU在不断切换线程执行。时间片一般是几十毫秒
  • 上下文切换:CPU在执行完当前任务的时间片后,会先保存这个任务的状态,然后才切换到下一个任务。其实就是任务从保存到再加载的过程,切换的代价就是影响多线程的执行速度

并发编程的效率就比串行执行高?

从图中我们可以看出达到百万级别以上的并发量,并发执行才有性能上的优势。主要是因为线程有创建和上下文切换的开销

减少上下文切换的措施

  • 无锁并发编程:多线程竞争锁时,会引起上下文切换,锁不存在就可以避免这种情况,可以将数据的id按照哈希算法取模分段,不同的线程处理不同段多的数据。
  • CAS算法:Java的Atomic包使用CAS算法来更新数据,不需要加锁,也就避免了上述情况。
  • 只使用必要的线程:任务很少但线程很多会造成大量线程处于等待状态
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换,这里使得线程之间的切换变成了线程内任务之间的切换

1.2 死锁

锁的应用场景很多,使数据变得相对安全,但锁也有一些问题,包括死锁。一旦出现死锁,就会造成系统功能不可用。

  • 死锁,简单地说就是两个线程互相持有对方需要的资源,双方都在等待对方释放锁,陷入了僵持。

死锁的场景很多,如线程拿到了锁,但在释放锁之前抛出了异常导致无法释放锁,也有因为陷入了死循环而持续占有锁。

避免死锁的方法

  • 避免一个线程同时获取多个锁,锁之间的关系相对简单较好
  • 避免一个线程在锁内同时占有多个资源
  • 尝试使用定时锁,使用lock.tryLock(timeout)来代替内部锁机制
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况

资源限制

软硬件资源的限制,如服务器带宽只有2Mb/s,资源的下载速度为1Mb/s,开十个线程不会变成10Mb/s。硬件资源限制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。软件资源限制有数据库的连接数,socket连接数等

资源限制引发的问题

如果并发执行的代码,因为受限于资源,依然在串行执行,效果甚至会不如直接串行执行,因为多了上下文切换和资源调度的时间

解决资源限制的问题

对于硬件资源限制,可以使用ODPS、Hadoop或其他服务器集群,使用哈希算法取模分段,然后不同的机器处理不同段的数据。

继续阅读