线程的存在就是因为进程再创建和销毁的时候申请和释放内存资源比较低效。线程创建后就能共享内存资源不用重复申请新的资源。
但如果线程创建的速率变得频繁,此时线程的开销也不能忽略!
此时就可以使用线程池优化这里的速度!
创建线程,是要在操作系统的内核中完成的。涉及到用户态->内核态的切换操作。这个操作有一定的开销。
线程池就是创建很多线程,当需要执行任务的时候,就不需要创建线程了,直接从池子里取个现成的。用完了也不需要释放线程,直接还回线程池中。
此处创建线程池并没有像正常创建实例一样new,而是通过另外Executors类的静态方法newCachedThreadPool来完成。这种创建实例的方式叫工厂模式。
构造实例时,主要是使用构造方法new.
而new的过程中,需要调用构造方法。有时候希望类提供多种构造实例的方法,就需要重载构造方法,来实现不同的版本的对象创建。但重载要求参数个数/类型不同,就存在一定的限制。
像此处的两个版本的构造方法就无法重载。
解决这个问题,可以用普通方法代替构造方法。使用普通方法,在里面构造出Point对象。
一、标准库的线程池
由于构造方法,方法名必须和类名相同,所以只能通过重载来完成,换成普通方法,直接通过方法名来完成,这样也不用考虑重载问题了。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo26 {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("这是任务");
}
});
}
}
这里使用submit方法,把任务交给线程池就行。
二、实现线程池
线程池中,需要把N个任务分配给M个线程,这需要生产者消费者模型。
先创建一个阻塞队列,每个被提交的任务,都被放到阻塞队列中。然后建立M个线程取队列元素,如果队列空了,M个线程阻塞等待。
如果队列不为空,每个线程取任务,执行任务,再取下一个.....直至队空,线程阻塞。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyThreadPool {
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
public MyThreadPool(int m) {
//在构造方法中,创建出M个线程,负责完成工作
for (int i = 0; i < m; i++) {
Thread t = new Thread(() -> {
while (true) {
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
}
public class Demo27 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(10);
for (int i = 0; i < 1000; i++) {
int taskId = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" + taskId + "当前线程:" + Thread.currentThread().getName());
}
});
}
}
}
三、线程池的执行流程和拒绝策略:
线程池的执行流程:
(1)先判断当前线程数是否大于核心线程数,如果结果为true,则新建线程并执行任务
(2)如果结果为true,则判断任务队列是否已满,如果结果为fasle,就把任务添加到任务队列中等待线程执行
(3)如果结果为true,则判断当前线程数量是否超过最大线程数,如果结果为false,则新建线程执行次任务
(4)如果结果为true,就执行拒绝策略。
拒绝策略:
AbortPoliy:中止策略,线程池会抛出异常并中止执行此次任务
CallerRunPolicy:把任务交给添加此次任务main线程来执行
DiscardPolicy:忽略新任务,先执行旧任务
DiscardOldestPolicy:忽略最早的任务,执行新加入的任务