一、线程产生的原因:
直接使用new Thread创建线程有如下弊端:
- 每次new Thread新建对象性能差。
- 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
- 缺乏更多功能,如定时执行、定期执行、线程中断。
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在Java中可以通过线程池来达到这样的效果。从Java 5之后,Java开始支持线程池。使用线程池有如下好处:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
二、线程池的创建:
Java 5 新增一个Executors工厂类来产生线程池,该工厂类可以用静态工厂方法创建四种线程池:
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
三、线程池的使用步骤:
- 调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象表示一个线程池。
- 创建Runnable实现类或Callable实现类的实例,作为线程任务。
- 调用ExecutorService对象的submit()方法来提交Runnable和Callable实例。
- 当不需要提交任何任务时,调用ExecutorService对象的shutdown()来关闭线程池。
下面简单演示一下用法:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2); //创建线程池
//使用Lambda表达式创建Runnable对象
Runnable task = ()->{
for(int i = 0; i<100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
};
pool.submit(task); //提交线程
pool.submit(task);
pool.shutdown(); //关闭线程池
}
}