线程池
场景:
- 创建线程的代价虽然很低,但也没低到能直接忽视的程度,而每个连接都花费了这个代价
- 如果每个链接都创建一个线程,当请求连接的速度高于处理连接的速度时,系统的线程数也会随之快速增长,服务器将停止服务甚至奔溃。
来个线程池:
// 创建一个线程池,线程池的大小设为可用处理器数的2倍。
// 如果同一时间有超过线程池大小的execute()请求存在,超出的部分将进行排队直到某线程被释放。
int threadPoolSize = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
线程池大小
影响因素有:硬件性能,线程任务是CPU密集型还是IO密集型,是否有其他任务在同时运行等。
针对CPU密集型的任务:线程池大小应接近于可用核数;
针对IO密集型的任务:线程池可以设置得更大些。
写入时复制
CopyOnWriteArrayList
CopyOnWriteArrayList
使用了保护性复制的策略。它并不是在遍历列表前进行复制,而是在列表被修改时进行,已经投入使用的迭代器会使用当时的旧副本。
比如改变某个元素的值时。
阻塞队列
场景:
经典模式——生产者-消费者(producer-consumer)模式
在于生产者和消费者可能不会(几乎肯定不会)保持相同的速度,那么采用阻塞队列,阻塞队列只允许生产者的速度在一定程度上超过消费者的速度,但不会超过很多。
ArrayBlockingQueue
ArrayBlockingQueue
是一个并发队列,适合实现生产者-消费者模式。其提供了高校的并发方法put()
和 take()
,这些方法会在必要时阻塞:当时一个空队列调用take()
时,程序会阻塞直至队列变成非空;当对一个满队列调用put()
时,程序会阻塞直到队列有足够空间。
仿写阻塞队列。
在想想
- 阅读ForkJoinPool的文档——fork/join框架与线程池有什么区别?分别适用于什么场景?
- 什么是work-stealing?它适用于什么场景?如何用java.util.concurrent包提供的工具实现work-stealing?
- CountDownLatch和CyclicBarrier有什么区别?分别适用于什么场景?