线程存在的意义:使用进程实现并发编程(进程太重了),此时引入线程.线程叫做"轻量级进程",创建线程比创建进程更高效;销毁线程比销毁进程更高效;调度线程比调度进程更高效,此时,使用多线程就可以在很多时候代替进程来实现并发编程
但频繁创建销毁线程的时候,开销是比较大的
此时Java就提供了线程池(降低创建/销毁线程的开销)
创建/销毁线程,是由操作系统内核完成
从池子里获取/归还池,是用户代码就能实现的,不必交给内核操作
- 内核态进行的操作都是在操作系统内核中完成的.内核会给程序提供一些API,称为系统调用,程序可以调用系统调用,驱使内核完成一些工作,而系统调用里面的内容是直接和内核的代码相关的,这一部分工作不受程序员自身控制,都是内核自行完成的.
- 用户态执行的是程序员自己写的代码
相比于内核来说,用户态,程序执行的行为是可控的 如果是通过内核,从系统创建线程,就需要通过系统调用,让内核执行(内核不是只给你一个程序服务,而是给所有的程序都要提供服务). 因此,当使用系统调用,执行的代码的时候,无法确定内核都要做那些工作,整体过程"不可控"的.
Executor 是接口(没有实现任何的类),而ThreadPoolExecutor 类就是Executor的实现类,但ThreadPoolExecutor类在使用上不是很方便,在实例化时需要传入多个参数,以及考虑线程的并发数与线程池运行效率有关的参数,所以使用Executors工厂类创建线程对象,该类对创建ThreadPoolExecutor线程池进行封装
public class Thread27 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 1000; i++) {
int n = i;
pool.submit(() ->{
System.out.println("hello " + n);
});
}
}
}
运行程序之后,main线程结束,但是整个进程没结束,线程池中的线程都是前台线程.此时会阻止进程的结束
可以认为,这1000子任务,就在一个队列中排队,这5个线程,就依次来取队列中的任务,取一个执行一个,执行完之后再执行下一个
工厂模式 (使用普通的方法来代替构造方法,创建对象)
Executors.newFixedThreadPool(5),使用Executors类的静态方法,直接构造出一个对象,像这样的方法,就称为"工厂方法",提供这个工厂方法的类,Executors就成称为"工厂类".
Executors创建线程池的几种方式
corePoolSize 核心线程数
maximumPoolSize 最大线程数
long keepAliveTime 描述了临时工可以摸鱼的最大时间
TimeUnit unit 时间单位(s,ms,分钟)
BlockingQueue workQueue线程池的任务队列
ThreadFactory threadFactory 用于创建线程,线程池是需要创建线程的
RejectedExecutiomHandler handler 描述了线程池的"拒绝策略",也是一个特殊的对象,描述了当线程池任务队列满了,如果继续添加任务会有啥样的行为
ThreadPoolExecutor 相当于把里面的线程分成两类:
正式员工
临时工/实习生
允许正式员工摸鱼,不允许实习生摸鱼,如果实习生摸鱼摸的久了,就会被销毁,
整体的策略:正式员工保底,临时工动态调节
一个线程池,里面至少有两个大的部分
class MyThreadPool{
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public MyThreadPool(int n){
//创建出线程
for (int i = 0; i < n; i++) {
Thread t = new Thread(() ->{
while (true){
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
//注册任务给线程池
public void submit(Runnable runnable){
try {
queue.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Thread28 {
public static void main(String[] args) {
MyThreadPool pool = new MyThreadPool(10);
for (int i = 0; i < 1000; i++) {
int n = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello " + n);
}
});
}
}
}