线程与进程
进程是调度操作系统资源的最小单位。线程是调度CPU的最小单元,也叫轻量级进程,在一个进程里可以创建多个线程,每个线程都拥有各自的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。
线程类型
1.用户级线程(ULT)
在用户程序中实现的线程,不依赖操作系统核心,应用进程利用线程库提供创建,同步,调度和管理线程的函数来控制用户线程。不需要用户态/核心态切换,速度快。操作系统内核不知道多线程的存在,因此一个线程阻塞将使整个进程(包括它的所有线程)阻塞。
2.内核级线程(KLT)
线程的所有管理操作由操作系统内核完成,内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,内核可以分派属于同一进程的多个线程在多个处理器上运行。线程的创建,调度和管理由内核完成,相对用户级线程要慢,但比进程的创建和管理操作要快。
JAVA是依赖于底层操作系统,是基于内核级线程,通过JVM调用系统库创建内核线程,内核线程与JAVA-Thread是1:1的映射关系
线程池的意义
线程是一种稀缺的资源,它的创建与销毁是一个相对偏重且耗资源的操作,而java线程依赖于内核线程,其线程的创建需要进行操作系统状态切换,未避免资源过度消耗需要设法重用线程执行多个任务。线程池就是一个线程缓存,负责对线程进行统一分配,调优与监控。
什么时候使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大
1、线程池的优势
(1)、降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
(2)、提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
(3)方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
(4)提供更强大的功能,延时定时线程池。
2、线程池的主要参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1、corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
2、maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
3、keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
4、workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
5、threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
6、handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略
3、线程池流程
1、判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务。已满则。
2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。
3、判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和策略。
(1、判断线程池中当前线程数是否大于核心线程数,如果小于,在创建一个新的线程来执行任务,如果大于则
2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。
3、判断线程池中当前线程数是否大于最大线程数,如果小于,则创建一个新的线程来执行任务,如果大于,则执行饱和策略。)
4、线程池为什么需要使用(阻塞)队列?
1、因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换。
2、创建线程池的消耗较高。
5、线程池为什么要使用阻塞队列而不使用非阻塞队列?
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。
当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。
使得在线程不至于一直占用cpu资源。
自定义线程池:
package com.mzd.multipledatasources.test;
/**
* 自定义线程池练习,这是任务类,需要实现Runnable;
包含任务编号,每一个任务执行时间设计为0.2秒
* @author admin
*
*/
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"+id);
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程"+name+"执行完任务:ID"+id);
}
@Override
public String toString() {
return "MyTask [id=" + id + "]";
}
}
package com.mzd.multipledatasources.test;
import java.util.List;
/**
* 编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;
设计一个集合,用于保存所有的任务;
* @author admin
*
*/
public class MyWorker extends Thread {
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();
}
}
}
package com.mzd.multipledatasources.test;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* 成员变量:
1:任务队列 集合 需要控制线程安全问题
2:当前线程数量
3:核心线程数量
4:最大线程数量
5:任务队列的长度
成员方法
1:提交任务;
将任务添加到集合中,需要判断是否超出了任务总长度
2:执行任务;
判断当前线程的数量,决定创建核心线程还是非核心线程
* @author admin
*
*/
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+" 被缓存了...");
}
}
}
package com.mzd.multipledatasources.test;
/**
* 1: 创建线程池类对象;
2: 提交多个任务
* @author admin
*
*/
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);
}
}
}