一、线程池的作用
线程池的作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;
少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,
其他线程排队等候。一个任务执行完毕,再从队列中取最前面的任务开始执行。
若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,
如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
二、线程池的优点
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开得越多,消耗的内存也就越大,最后死机。)
三、Executors
Java 里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor 是 Executors 类的底层实现。
线程池创建线程大致可以分为三种:
- 固定大小连接池
使用多线程需要实现Runnable 接口对象,这里直接继承Thread 。
因为Thread也实现了 Runnable 接口
public class MyThread extends Thread
{
@Override
public void run(){
System.out.println(currentThread().getName()+" is Running ...");
}
}
public static void main(String args)
{
//创建一个可重用且固定线程数的线程池
int pool_num = 2 ;
//固定大小连接池 :不管 execute 执行几次,线程池始终都会使用 pool_num 个线程来处理
ExecutorService pool = Executors.newFixedThreadPool(pool_num);
// pool_num 等于2 ,我们启动4个线程来测试
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
//关闭线程池
pool.shutdown();
}
运行结果
pool-1-thread-1 is Running ...
pool-1-thread-1 is Running ...
pool-1-thread-1 is Running ...
pool-1-thread-2 is Running ...
从上面的运行来看,我们 Thread 类都是在线程池中运行的,线程池在执行 execute 方法来执行 Thread 类
中的 run 方法。不管 execute 执行几次,线程池始终都会使用 2 个线程来处理。不会再去创建出其他线程来处理run 方法执行。这就是固定大小线程池。
- 单任务连接池
ExecutorService pool = Executors.newSingleThreadExecutor();
Thread t1 = new MyThread();
Thread t2 = new MyThread();
pool.execute(t1);
pool.execute(t2);
pool.shutdown();
运行结果
pool-1-thread-1 is Running ...
pool-1-thread-1 is Running ...
不管 execute 执行几次,线程池始终都会使用单个线程来处理。
- 可变连接池
ExecutorService pool = Executors.newCachedThreadPool();
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
//将线程放入线程池中执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
//关闭线程池
pool.shutdown();
运行结果
pool-1-thread-1 is Running ...
pool-1-thread-2 is Running ...
pool-1-thread-3 is Running ...
pool-1-thread-4 is Running ...
这里 execute 执行多次,线程池就会创建出多个线程来处理 Thread 类中 run 方法。
我们看到连接池会根据执行的情况,在程序运行时创建多个线程来处理,这里就是可变连接池的特点。
四、ExecutorService 执行器服务
java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务。
因此一个 ExecutorService 很类似于一个线程池。实际上,存在于 java.util.concurrent 包里的 ExecutorService 实现就是一个线程池实现。
//线程工厂类创建出线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//执行一个线程任务
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
//线程池关闭
executorService.shutdown();
上面代码首先使用 newFixedThreadPool() 工厂方法创建一个 ExecutorService。
这里创建了一个固定大小为10的线程执行任务的线程池。然后,将一个 Runnable
接口的匿名实现类传递给 execute() 方法。
这将导致 ExecutorService 中的某个线程执行该 Runnable。
这里可以看成一个任务分派,示例代码中的任务分派我们可以理解为:
- 一个线程将一个任务委派给一个 ExecutorService 去异步执行。
- 一旦该线程将任务委派给 ExecutorService,该线程将继续它自己的执行,独立于该任务的执行。