线程池
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");
}
});
}
}
效果