线程池作用就是限制系统中执行线程的数量。 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。
import java.util.Scanner;
public class NoThreadPool {
static class Deliver extends Thread {
String task;
public Deliver(String task) {
this.task = task;
}
@Override
public void run() {
System.out.println("开始进行任务: " + task);
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务完成: " + task);
}
}
public static void main(String[] args) {
// 主线程就是前台
Scanner scanner = new Scanner(System.in);
while (true) {
String task = scanner.nextLine();
Thread worker = new Deliver(task);
worker.start();
}
}
}
没有线程池时,每次执行任务,都要重新开辟一个线程,造成资源浪费。
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class HasThreadPool {
static class Tasks implements Runnable {
String task;
public Tasks(String task) {
this.task = task;
}
@Override
public void run() {
System.out.println("开始执行任务: " + task);
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务结束: " + task);
}
}
public static void main(String[] args) {
// 容纳了10个线程的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
// 主线程就是前台
Scanner scanner = new Scanner(System.in);
while (true) {
String thing = scanner.nextLine();
Tasks task = new Tasks(thing);
pool.execute(task);
}
}
}
注意:
线程池,一开始不会创建好所有的线程。随着线程任务被提交,他会判断当前需要开启线程数是否小于正式线程数,如果小于,则创建线程。随着每加一个任务,就会随之创建一个线程,当达到正式线程数时就不会再创建了。线程创建好后,如果没有任务就会等待任务到来。当线程处理不了当前任务时,则将任务放到阻塞队列中等待空闲线程来取。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SomeSimpleThreadPool {
public static void main(String[] args) {
ExecutorService p1 = Executors.newFixedThreadPool(10);
ExecutorService p2 = Executors.newCachedThreadPool();
}
}
固定长度的线程池和临时线程池返回的都是一个ThreadPoolExecutor类
。
Executors.newFixedThreadPool(10):
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newFixedThreadPool不允许有临时线程,其堵塞队列为无限长的链表式队列。
Executors.newCachedThreadPool()
:public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool()不允许有正式线程,临时线程的数量不限制,SynchronousQueue()是一个容量为1的堵塞队列
下面是它的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:
正式线程的最大数量maximumPoolSize:
正式线程+临时线程的最大数量keepAliveTime:
这两个表示允许临时线程的最大空闲时间TimeUnit unit:
keepAliveTime的时间单位BlockingQueue workQueue:
传递任务的堵塞队列ThreadFactory threadFactory:
创建线程的工厂RejectedExecutionHandler handler:
线程太多时的解决方案(拒绝处理任务时的策略方法execute(Runnable)中提交的新任务将在执行程序关闭时被拒绝,并且当执行程序对最大线程和工作队列容量使用有限边界并且饱和时。提供了四个预定义的处理程序策略:
1) ThreadPoolExecutor.AbortPolicy(默认)::丢弃任务并抛出RejectedExecutionException
异常。
2) ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
3) ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务。
3) ThreadPoolExecutor.CallerRunsPolicy:由调用execute本身的线程(主线程)运行任务。 提供了一个简单的反馈控制机制,降低了新任务提交的速度。
5) 可以定义和使用其他类型的RejectedExecutionHandler类。
主线程负责提交任务——execute(Runnable对象),当正式线程超过最大正式线程数时,且堵塞队列已经满了时,就可以创建临时线程,临时线程的最大空闲时间为keepAliveTime,如果所有的线程都不能处理任务了,那么就会执行拒绝策略RejectedExecutionHandler。
具体流程:
ExecutorService 引用可以指向一个线程池对象
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
// 创建了一个
// 最多有 10 个正式员工
// 最多有 20 个员工包括正式和临时
// 临时工空闲时间最多有 60 SECONDS
// 传递任务的队列是一个 ArrayBlockingQueue(容量是 30)
// 执行默认的拒绝策略:处理不过来之后抛出异常
ExecutorService p1 = new ThreadPoolExecutor(
10,
20,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(30)
);
// ExecutorService 引用可以指向一个线程池对象
// 怎么提交任务
p1.execute(new Runnable() {
@Override
public void run() {
}
});
//或者
//提交一个可运行的任务执行
p1.submit(new Runnable() {
@Override
public void run() {
}
});
// 怎么关闭线程池
p1.shutdown(); // 推荐使用上面的
p1.shutdownNow();
}
}
注意:
submit 返回一个Future对象,可以通过这个对象来获得工作线程执行的结果。
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class CustomThreadPool {
static class Worker extends Thread {
private BlockingQueue<Runnable> workQueue;
Worker(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}
@Override
public void run() {
while (!Thread.interrupted()) {
Runnable task = null;
try {
// 从队列中取任务,当队列为空时,线程就会阻塞,否则就一直工作
task = workQueue.take();
} catch (InterruptedException e) {
break; // 收到中断通知,退出
}
// 完成任务,run是Runnable中的方法
task.run();
}
}
}
// 1. 传递任务用的阻塞队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
// 2. 记录所有的线程对象
List<Worker> workerList = new ArrayList<>();
//nThreads指定固定线程池的线程数量
CustomThreadPool(int nThreads) {
// 创建所有的工作线程
for (int i = 0; i < nThreads; i++) {
Worker worker = new Worker(workQueue);
worker.start();
workerList.add(worker);
}
}
// 主线程要做的就是把接到的任务放到队列中即可
// 工作线程会在合适的时候过来取任务的
void execute(Runnable task) throws InterruptedException {
workQueue.put(task);
}
void shutdown() {
// 让所有的线程都停止工作
// 可以使用 interrupt 建议一个线程停下来
for (Worker worker : workerList) {
worker.interrupt();
}
}
//测试
public static void main(String[] args) throws InterruptedException {
CustomThreadPool pool = new CustomThreadPool(3);
int i=0;
while(i<10){
pool.execute(() -> {
try {
System.out.println("暂停5s");
Thread.sleep(5*1000);
System.out.println("暂停结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
i++;
}
}
}