并发最多见用于线程池,显然使用线程池能够有效的提升吞吐量。html
最多见、比较复杂一个场景是Web容器的线程池。Web容器使用线程池同步或者异步处理HTTP请求,同时这也能够有效的复用HTTP链接,下降资源申请的开销。一般咱们认为HTTP请求时很是昂贵的,而且也是比较耗费资源和性能的,因此线程池在这里就扮演了很是重要的角色。java
在线程池的章节中很是详细的讨论了线程池的原理和使用,同时也提到了,线程池的配置和参数对性能的影响是巨大的。不尽如此,受限于资源(机器的性能、网络的带宽等等)、依赖的服务,客户端的响应速度等,线程池的威力也不会一直增加。达到了线程池的瓶颈后,性能和吞吐量都会大幅度下降。redis
一直增长机器的性能或者增大线程的个数,并不必定能有效的提升吞吐量。高并发的状况下,机器的负载会大幅提高,这时候机器的稳定性、服务的可靠性都会降低。sql
尽管如此,线程池依然是提升吞吐量的一个有效措施,配合合适的参数可以有效的充分利用资源,提升资源的利用率。apache
除了线程池是比较发杂的并发场景外,任务队列也是一个不错的并发工具。JDK内部有大量的队列(Queue),这些工具不只可以方便使用,提升生产力,也可以进行组合适应于不一样的场景。即便线程池内部,也是用了任务队列来处理任务的积压,平衡资源的消耗。安全
安全的任务队列可以有效的平衡机器的复杂,抵消因为峰值和波动带来的不稳定,有效提升服务的可靠性。同时任务队列的处理也有助于统计和分析服务的情况。网络
任务队列也能够在多个线程之间传递数据,有助于并行处理任务。例如经典的“生产者-消费者”模型就能够有效的提升多个线程的并行处理能力。在IO延时比较大的服务中尤为有效。 我最喜欢的一个案例是导数据是,一个线程负责往固定大小的任务队列中压入大量的数据,队列满了之后就暂停,另外几个线程负责从任务队列中获取数据并消费。这将串行的“生产-消费”,变成了并行的“生产-消费”。实践证实极大的节省任务处理时间。多线程
线程池也是异步处理的一种表现形式,除此以外,使用异步处理的目的也是为了提升服务的处理速度。 例如AOP的一个例子就是使用切面来记录日志,若是说咱们要远程收集日志,显然不但愿因为收集日志而影响服务自己。这时候就将日志收集的过程进行异步处理。并发
现在大量的开源组件都喜欢使用异步处理来提升IO的效率,某些不须要同步返回的操做使用异步处理后可以有效的提升吞吐量。异步
固然,异步也不老是使人满意的,也会有相应的问题。例如引入异步设计后的复杂性,线程中断后的处理机制,失败后的处理策略,产生的消息比消费的还快时怎么办,关闭程序时如何关闭异步处理逻辑等等。这都会增长系统的复杂性。
尽管大量的服务、业务使用异步来处理,可是很显然须要有保障机制可以保证异步处理的逻辑正确性。若是认为异步处理的任务不是特别重要,或者说主业务不能由于附属业务的逻辑出错而崩溃,那么使用异步处理是正确的选择。
并发操做的同时还须要维护数据的一致性,或多或少的会涉及到同步操做。正确的使用原子操做,合理的使用独占锁和读写锁也是一个很大的挑战。
线程间的协调与通讯,尤为是状态的同步都是比较困难的。咱们看到线程池ThreadPoolExecutor的实现为了解决各个线程的执行状态,引入的不少的同步操做。线程愈来愈多的状况下,同步的成本会愈来愈高,同时也有可能引入死锁的状况。
尽管如此,单个JVM内部的多线程同步仍是比较容易控制的。JDK内部也提供了大量的工具来方便完成数据的同步。例如Lock/Condition/CountDownLatch/CyclicBarrier/Semaphore/Exchanger等等。
分布式的并发问题更难以处理,根据CAP的原理,基本上没有一个至善至美的方案。 分布式资源协调使用分布式锁是一个不错的选择。Google的分布式锁(创建在BigTable之上),Zookeeper的分布式锁,甚至简单的利用memcache的add操做或者redis的setnx操做创建伪分布式锁也能够解决相似的问题。