概述
线程池就是一个可以复用线程的技术。
不使用线程池的问题:
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了又要创建新线程处理,而创建新线程的开销很大,而且请求过多会产生大量的线程出来,会严重影响系统性能。
核心线程:一直工作的线程数。
任务队列:实现了Runnable、Callable的接口
创建线程池
ExecutorService接口
注意事项:
新任务提交时发现核心线程都在忙,任务队列也满了,而且还可以创建临时线程,这时才会创建临时线程。
核心线程和临时现场都满了,任务队列也满了,还有新任务到来,就会拒绝新任务。
创建线程池执行Runnable任务
public class test {
public static void main(String[] args) {
// 1. 通过ThreadPoolExecutor创建一个线程池对象。
ExecutorService pool = new ThreadPoolExecutor(
3, // corePoolSize: 核心线程数
5, // maximumPoolSize: 最大线程数
8, // keepAliveTime: 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(4), // 阻塞队列
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
Runnable target = new MyRunnable();
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 复用线程池里的线程
pool.execute(target); // 复用线程池里的线程
// pool.shutdown(); // 关闭线程池:等待全部任务执行完毕后,再关闭线程池
// pool.shutdownNow(); // 立即关闭线程池:不管任务是否已执行完毕!
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
创建临时线程时机
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//创建临时线程
pool.execute(target);
pool.execute(target);
拒绝新任务时机
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//创建临时线程
pool.execute(target);
pool.execute(target);
pool.execute(target);//拒绝
创建线程池处理Callable任务
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 通过ThreadPoolExecutor创建一个线程池对象。
ExecutorService pool = new ThreadPoolExecutor(
3, // corePoolSize: 核心线程数
5, // maximumPoolSize: 最大线程数
8, // keepAliveTime: 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(4), // 阻塞队列
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// Runnable target = new MyRunnable();
//
// pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
// pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
// pool.execute(target); // 会分配自动创建一个新线程,自动处理这个任务,自动执行的!
// pool.execute(target);
// pool.execute(target);
// pool.execute(target);
// pool.execute(target);
// //创建临时线程
// pool.execute(target);
// pool.execute(target);
// pool.execute(target);//拒绝
// 2. 使用线程处理Callable任务。
Future<String> f1 = pool.submit(new MyCallable(100));
Future<String> f2 = pool.submit(new MyCallable(200));
Future<String> f3 = pool.submit(new MyCallable(300));
Future<String> f4 = pool.submit(new MyCallable(400));
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
// pool.shutdown(); // 关闭线程池:等待全部任务执行完毕后,再关闭线程池
// pool.shutdownNow(); // 立即关闭线程池:不管任务是否已执行完毕!
}
}
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
//线程任务,返回结果
// 需求:求1~n的和,并返回
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return "1~" + n + "的和为:" + sum;
}
}
Executors工具类实现线程池
Executors是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
可以更方便创建,不用写七个变量。但是,大型并发系统不允许使用这种方法。
核心线程数配置
计算密集型:核心线程数=CPU核数+1;
IO密集型:核心线程数=CPU核数*2.
其他
并发和并行
进程:运行的一个程序就是一个独立的进程。
线程属于进程的,一个进程中可以运行很多线程。
并发:进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程数量有限,为了保证所有线程都能进行,CPU会轮询为系统的每个线程服务,由于CPU的切换速度很快,给我们感觉在同时进行,称之为并发。
并行:在同一时刻,同时有多个线程在被CPU调度执行。这是真正在同时执行。
多线程执行是并发和并行同时执行。
线程的生命周期
线程从生到死的过程中,经历的各种状态及状态转换。
理解线程这些状态有利于提升并发编程的理解能力。
java状态有六种