线程池
线程池
1、创建一个空的任务容器
2、在容器中初始化10个执行任务的线程,俗称消费者线程
3、最开始这个容器是空的,所以线程都wait在上面
4、直到一个外部线程往这个容器扔了一个“任务”,这时候就会唤醒消费者线程notify
5、这个消费者线程从容器中取出”任务“,并且执行这个任务,任务完成,wait等待下一次任务到来
6、如果短时间有很多任务在容器,就会唤醒所有线程notify,去执行这些任务
在整个过程中,只需要在初始化池子的时候,会加载线程。之后都不会创建线程,而是循环使用已经存在的线程
自定义一个线程池
ThreadPool
package com.thread.thread16; import java.util.LinkedList; public class ThreadPool { //线程池大小 int threadPoolSize; //任务容器 这个是链表容器 Runnable是线程接口 创建线程需要实现这个接口 LinkedList<Runnable> tasks = new LinkedList<Runnable>(); //试图消费任务的线程 public ThreadPool(){ //构造函数 threadPoolSize = 10; //线程池的大小 //启动10个任务消费者线程 synchronized (tasks) {//锁定这个任务 for(int i=0; i<threadPoolSize; i++) { //遍历线程池子的线程 new TaskConsumeThread("任务消费者线程" + i).start(); //挨个启动线程 俗称初始化 } } } public void add(Runnable r) { synchronized (tasks) { //锁定这个任务容器 tasks.add(r); //将任务添加到容器中 //唤醒等待的任务消费者线程 tasks.notifyAll(); //将存放任务的容器内部线程全部唤醒 } } class TaskConsumeThread extends Thread { //内部线程类 public TaskConsumeThread(String name) { //构造函数 给线程取名字 super(name); } Runnable task; //单个线程 public void run() { //重写runnable接口中的run方法 System.out.println("启动:" + this.getName()); //打印哪个线程被初始化 while(true) { synchronized (tasks) { //锁定任务容器 while(tasks.isEmpty()) { //查看任务容器是否有任务 空就返回true 执行wait try { tasks.wait(); //没有任务 让线程们等一下下 }catch (InterruptedException e) { e.printStackTrace(); } } //这个时候有任务 tasks不为空 task = tasks.removeLast(); //删除最后一个元素 并且返回元素 这个时候是派遣任务给线程 //允许添加任务的线程可以继续添加任务 tasks.notifyAll(); } System.out.println(this.getName() + "获取到任务,并执行"); //看谁抢到这个任务 task.run(); //线程争抢该任务 } } } }
案例一:测试线程池抢占一个任务
package com.thread.thread16; public class TestThread { public static void main(String[] args) { ThreadPool pool = new ThreadPool(); for(int i=0; i<5; i++) { //放五个任务 Runnable task = new Runnable() { //任务 @Override public void run() { // System.out.println("执行任务"); //执行任务的代码 //可以打印一段话 //访问一个文件 //可能做排序 } }; pool.add(task); //将任务放入 try { Thread.sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); } } } }
效果
案例二:隔一定时间添加任务到容器中,保证每个线程都被利用到
package com.thread.thread16; public class TestThread2 { public static void main(String[] args) { ThreadPool pool = new ThreadPool(); //创建线程池子 int sleep = 1000; //睡眠时间 while(true) { pool.add(new Runnable() { //池子里面去扔任务 @Override public void run() { System.out.println("执行任务"); try{ Thread.sleep(1000); //每个任务 执行久一点 1秒钟 这样 就能看到其他线程也能一起抢占任务 }catch (InterruptedException e) { e.printStackTrace(); } } }); try { Thread.sleep(sleep); sleep = sleep > 100 ?sleep-100:sleep; //如果执行时间大于100秒 则每次减100 直到减到100以下 直接返回sleep }catch (InterruptedException e) { e.printStackTrace(); } } } }
效果
案例三:java自带的线程池
package com.thread.thread16; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class TestThread3 { public static void main(String[] args)throws InterruptedException { //使用自带线程池子 //10 代表线程池子中初始化了10个 //15 代表如果10个线程不够了的话 就扩容到15 //如果60秒 扩容的5个处于空闲 则回收线程 池子保持10个 //linkedBlockingQueue 任务队列 放任务的地方 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); //线程执行 threadPool.execute(new Runnable() { //runnable是接口 这里实例化的是runnable接口的实现类 @Override public void run() { //重写run方法 System.out.println("任务1"); } }); } }
效果