线程池
Java中的线程池是运用最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。
线程池的好处/作用:
1.不需要频繁的创建和销毁线程,提高相应速度
2.管理线程,避免无休止的创建线程导致资源枯竭。(不会每来一个任务就创建一个线程,线程不断的取任务执行)
线程池的方法有哪些
五个方法:
void execute(Job job): 执行一个Job,这个Job需要实现Runnable接口
void shutdown():关闭线程池
void addWorkers(int num):添加工作线程
void removeWorkers(int num):减少工作者线程
int getJobsSize():获得正在等待执行的任务数量
线程池的提交方式有哪几种
两种。第一种是execute不带返回值的提交,第二种是submit带返回值的提交。
线程池的属性有哪些
最大线程数 核心线程数 任务队列 超时时间 拒绝策略
核心线程数: 提交一个任务到线程池的时候,线程池会创建工作线程执行任务,即使其他核心线程空闲也会创建,直到达到核心线程数。
任务队列: 用来保存正在等待执行的任务的阻塞队列。
最大线程数: 线程池允许创建最大的线程数量。如果任务队列满了,并且已创建的线程数小于最大线程数,就会创建新的线程加速消耗。
超时时间: 如果在指定时间内没有任务那么线程就会撤销,不再占用CPU资源。
拒绝策略: 线程和队列都满了的情况下采取一种策略来应对到来的任务。
拒绝策略有哪些(可以说出来两三个)
直接抛异常/只用调用者所在线程来运行任务/丢弃队列中最近的一个任务,并执行当前任务/不处理把任务丢掉。
线程池的工作流程
1.判断核心线程是否已经满了。如果满了,下一步,没满就创建线程执行任务。
2.任务队列是不是满了。如果没满,把任务加入到队列中,如果满了,下一步。
3.线程池是不是满了。如果满了,采取拒绝策略,如果没满就创建新的线程加速消耗。
总结: 有任务过来先给到核心线程,核心线程处理不过来,就交给队列,队列满了,就开启最大线程数来加速消耗。
线程池初始阶段注意事项
即使一个任务提交时线程是空闲状态,只要是没达到核心线程数,就会创建新线程。
代码模拟
线程池接口
public interface ThreadPool<Job extends Runnable>{
void execute(Job job);
void shutdown();
void addWorkers(int num);
void removeWorkers(int num);
int getJobSize();
}
工作线程实现
因为jobs是多线程共享的资源,所以在使用的时候需要先上锁。
有任务就取出来任务执行,没任务就会阻塞在jobs上面。
class Worker implements Runnable {
//是否在工作
private volatile boolean running = true;
@Override
public void run() {
while (running) {//取任务
Job job = null;
synchronized (jobs) {
while (jobs.isEmpty()) {
try {
System.out.println("没任务了" + Thread.currentThread().getName() + "进入阻塞");
jobs.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
job = jobs.removeFirst();//取任务
}
if (job != null) {
job.run();//执行任务
}
}
}
public void shutdown() {
this.running = false;
}
}
线程池实现
public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
//最大的工作线程数量
private static final int MAX_WORKER_NUMBERS = 10;
//默认的线程数量
private static final int DEFAULT_WORKER_NUMBERS = 5;
//最小的工作线程数量
private static final int MIN_WORKER_NUMBERS = 1;
//任务集合
private final LinkedList<Job> jobs = new LinkedList<>();//任务从队尾插入 从队头取出来
//工作线程
private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
//线程的编号
private AtomicLong threadNum = new AtomicLong(0);
//工作线程数
private int workerNums = DEFAULT_WORKER_NUMBERS;
public DefaultThreadPool() {//初始化线程池
initializeWorkers(DEFAULT_WORKER_NUMBERS);
}
public DefaultThreadPool(int workerNums) {//指定线程
this.workerNums = workerNums > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : workerNums < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : workerNums;
initializeWorkers(this.workerNums);
}
private void initializeWorkers(int workerNums) {
for (int i = 0; i < workerNums; i++) {
Worker worker = new Worker();
workers.add(worker);//加入到集合当中
Thread thread = new Thread(worker, "worker thread" + threadNum.incrementAndGet());
System.out.println("输出了" + threadNum);
thread.start();//进入就绪态 开始工作
}
}
@Override
public void execute(Job job) {
if (job != null) {
synchronized (jobs) {
jobs.addLast(job);
jobs.notify();//唤醒因jobs阻塞的工作线程
}
}
}
@Override
public void shutdown() {//将线程池关闭
for (Worker worker: workers){
worker.shutdown();//停止工作
}
}
@Override
public void addWorkers(int num) {//添加num个工作线程
synchronized (jobs) {
if (num + workerNums > MAX_WORKER_NUMBERS) num = MAX_WORKER_NUMBERS - workerNums;
initializeWorkers(num);//添加num个线程
workerNums += num;
}
}
@Override
public void removeWorkers(int num) {
synchronized (jobs) {
if (num >= workerNums) {
throw new IllegalArgumentException("删除失败");
}
for (int i = 0; i < num; i++) {
Worker worker = workers.get(i);
if (workers.remove(worker)) {
worker.shutdown();//线程不再工作
}
}
workerNums -= num;//减去数量
}
}
@Override
public int getJobSize() {
return this.jobs.size();
}
}
创建测试类
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
ThreadPool threadPool = new DefaultThreadPool();
for(int i=0;i<10;i++){
Task task = new Task();
threadPool.execute(task);
}
Thread.sleep(1000);
threadPool.shutdown();
}
static class Task implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + " task is going...");
}
}
}
}
在这个过程中,可能会有一个误区。run和start是不同的,start会开启一个新线程,但是run就等同于调用了普通方法,并不会开启新线程。
所以在Thread.currentThread().getName()打印的实际上就是工作线程的名称。
所有内容参考自《java并发编程的艺术》
标签:jobs,实现,void,int,num,线程,简单,workerNums From: https://www.cnblogs.com/chengyiyuki/p/18067659