一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。
什么是线程,线程和进程的区别是什么?
线程:程序执行流的最小执行单位,是行程中的实际运作单位。
进程:动态的执行过程,活动的实体。
区别:一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。进程中包含了多个可以同时运行的线程。
线程生命周期
①:new(新建)、②:Runnable(就绪)、③:Running(运行)、④:Blocked(阻塞)、⑤:Terminated(结束)
为什么使用线程池?
根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力。线程池核心参数
- corePoolSize(核心线程大小):最小的线程数量;
- maximumPoolSize(线程池最大线程数量):最大线程数量;
- keepAliveTime(空闲线程存活时间):空闲线程存活时间;
- unit(空间线程存活时间单位):空间线程存活时间单位;
- workQueue(工作队列):存放任务队列;
jdk中包含四种工作队列模式:
①ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。新任务进来,排在队列末尾,有界数组可有效防止资源耗尽问题。当线程数量达到corePoolSize后,再有新任务进来,防止队列末尾,等待调度,队列已满,则创建新线程,若达至maxPoolSize,实行拒绝策略。
②LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。该队列近似无界性,当线程数量达到corePoolSize后,再有新任务进来,存入队列中,不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene:不缓存任务的阻塞队列,放入一个任务必须等到完成该任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。 - threadFactory(线程工厂):创建新线程的工厂,可以用来设定线程名、是否为daemon线程(守护线程)等;
守护线程:一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。 - handler(拒绝策略):当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,若还有新任务提交进来,执行该策略。
jdk中包含四种拒绝策略:
①AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满;
②DisCardPolicy:不执行新任务,也不抛出异常;
③DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行;
④CallerRunsPolicy:直接调用execute来执行当前任务。
优势:
1. 线程和任务分离,提升线程重用性;
2. 控制线程并发数量,降低服务器压力,统一管理所有线程;
3. 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
劣势:
1. 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
2. 多线程需要协调和管理,所以需要CPU时间跟踪线程;
3. 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
4. 线程太多会导致控制太复杂,最终可能造成很多Bug;
什么是单线程和多线程?
单线程:只有一个主线程的程序,称作单线程程序。每个正在运行的程序(即进程),至少包括一个线程,这个线程叫主线程。
多线程:拥有多个线程的程序,称作多线程程序。可以根据需要开辟若干子线程,子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。
自定义线程池-实现
- 编写任务类(MyTask),实现Runnable接口;
/*
需求:
自定义线程池练习,这是任务类,需要实现Runnable;
包含任务编号,每一个任务执行时间设计为0.2秒
*/
public class MyTask implements Runnable{
private int id;
//由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成
public MyTask(int id) {
this.id = id;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("线程:"+name+" 即将执行任务:"+id);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+name+" 完成了任务:"+id);
}
@Override
public String toString() {
return "MyTask{" +
"id=" + id +
'}';
}
}
- 编写线程类(MyWorker),用于执行任务,需要持有所有任务;
/*
需求:
编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;
设计一个集合,用于保存所有的任务;
*/
public class MyWorker extends Thread{
private String name;//保存线程的名字
private List<Runnable> tasks;
//利用构造方法,给成员变量赋值
public MyWorker(String name, List<Runnable> tasks) {
super(name);
this.tasks = tasks;
}
@Override
public void run() {
//判断集合中是否有任务,只要有,就一直执行任务
while (tasks.size()>0){
Runnable r = tasks.remove(0);
r.run();
}
}
}
- 编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;
/*
这是自定义的线程池类;
成员变量:
1:任务队列 集合 需要控制线程安全问题
2:当前线程数量
3:核心线程数量
4:最大线程数量
5:任务队列的长度
成员方法
1:提交任务;
将任务添加到集合中,需要判断是否超出了任务总长度
2:执行任务;
判断当前线程的数量,决定创建核心线程还是非核心线程
*/
public class MyThreadPool {
// 1:任务队列 集合 需要控制线程安全问题
private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
//2:当前线程数量
private int num;
//3:核心线程数量
private int corePoolSize;
//4:最大线程数量
private int maxSize;
//5:任务队列的长度
private int workSize;
public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
this.corePoolSize = corePoolSize;
this.maxSize = maxSize;
this.workSize = workSize;
}
//1:提交任务;
public void submit(Runnable r){
//判断当前集合中任务的数量,是否超出了最大任务数量
if(tasks.size()>=workSize){
System.out.println("任务:"+r+"被丢弃了...");
}else {
tasks.add(r);
//执行任务
execTask(r);
}
}
//2:执行任务;
private void execTask(Runnable r) {
//判断当前线程池中的线程总数量,是否超出了核心数,
if(num < corePoolSize){
new MyWorker("核心线程:"+num,tasks).start();
num++;
}else if(num < maxSize){
new MyWorker("非核心线程:"+num,tasks).start();
num++;
}else {
System.out.println("任务:"+r+" 被缓存了...");
}
}
}
- 编写测试类(MyTest),创建线程池对象,提交多个任务测试;
/*
测试类:
1: 创建线程池类对象;
2: 提交多个任务
*/
public class MyTest {
public static void main(String[] args) {
//1:创建线程池类对象;
MyThreadPool pool = new MyThreadPool(2,4,20);
//2: 提交多个任务
for (int i = 0; i <30 ; i++) {
//3:创建任务对象,并提交给线程池
MyTask my = new MyTask(i);
pool.submit(my);
}
}
}