作者简介: zoro-1,目前大二,正在学习Java,数据结构,javaee等
作者主页: zoro-1的主页
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
创建线程池
public class PoolText {
public static void main(String[] args) {
ExecutorService executorService= Executors.newFixedThreadPool(2);
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务1 "+Thread.currentThread());
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务2 "+Thread.currentThread());
}
});
}
}
一共有两大种创建线程池的方式
ThreadPoolExcutor类创建线程池
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
以上四种方法都是大同小异,这里我们挑选参数最多的进行讲解
public ThreadPoolExecutor(
// 线程池核心线程数
int corePoolSize,
// 线程池最大数
int maximumPoolSize,
// 空闲线程存活时间
long keepAliveTime,
// 时间单位
TimeUnit unit,
// 线程池所使用的阻塞队列
BlockingQueue<Runnable> workQueue,
// 线程池创建线程使用的工厂
ThreadFactory threadFactory,
// 线程池对拒绝任务的处理策略
RejectedExecutionHandler handler)
}
将线程池比作一个公司,当这个公司不忙时是10个员工(corePoolSize),忙碌起来就需要招实习生,假设招来5个实习生(maximumPoolSize=5+10),当公司不忙时,实习生就空闲下来了,这时就要裁掉实习生,但是是当实习生空闲到一定时间才裁掉,假设实习生空闲10小时就要裁掉他们(keepAliveTime=10,unit=h)
而这里的阻塞队列负责的就是存任务,threadFactory就是将创建线程对象的任务交给工厂类(将工厂方法(使用普通方法将构造方法封装起来)放进类),而这里的handler就是当队列满的时候如何处理新的任务加进来的情况。
这里为什么要用阻塞队列?
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。使得在线程不至于一直占用cpu资源。
处理决策
ThreadPoolExecutor是Java中的线程池实现类,它提供了四种处理策略:
-
AbortPolicy(默认策略):当线程池无法容纳新的任务时,会抛出RejectedExecutionException异常。
-
CallerRunsPolicy:当线程池无法容纳新的任务时,任务会退回给调用者执行,这样调用者就会阻塞。
-
DiscardOldestPolicy:当线程池无法容纳新的任务时,会丢弃队列中最老的一个任务,然后尝试再次提交新任务。
-
DiscardPolicy:当线程池无法容纳新的任务时,会直接丢弃任务,不做任何处理。
这些处理策略可以在创建ThreadPoolExecutor对象时通过参数设置。如果没有显式地指定,默认使用AbortPolicy策略。根据具体的业务需求,可以选择适合的处理策略。
Executors类创建线程池
-
newFixedThreadPool(int nThreads):创建一个固定大小的线程池,该线程池中的线程数始终为nThreads。
-
newCachedThreadPool():创建一个可缓存的线程池,该线程池中的线程数根据需要动态增加或减少,空闲线程会被保留60秒。
-
newSingleThreadExecutor():创建一个只有一个线程的线程池,该线程池保证所有任务按照提交顺序依次执行。
-
newScheduledThreadPool(int corePoolSize):创建一个定时任务线程池,该线程池可以定期执行任务或延迟执行任务。
这些方法返回的都是ExecutorService接口的实例,使用execute()方法来提交任务给线程池执行。
线程池的执行流程
线程池的执行流程:
1.当新加入一个任务时,先判断当前线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务;
2.如果结果为 true,则判断任务队列是否已满,如果结果为 false,则把任务添加到任务队列中等待线程执行
3.如果结果为 true,则判断当前线程数量是否超过最大线程数?如果结果为 false,则新建线程执行此任务
4.如果结果为 true,执行拒绝策略。
手搓一个线程池
过程:
1.创建一个构造方法,创建指定线程数,并让这些线程执行任务
2.有一个阻塞队列用来添加任务
3.创建一个submit方法添加新任务
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
class MyPool{
private List<Thread> list=new ArrayList<>();
private BlockingQueue<Runnable> blockingDeque=new ArrayBlockingQueue<>(1000);
//这里开启n个线程,这些线程不断的从阻塞队列的队首获取任务,一直执行
public MyPool(int n) throws InterruptedException {
for(int i=0;i<n;i++){
Thread t=new Thread(()->{
while (true){
Runnable runnable= null;
try {
runnable = blockingDeque.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
runnable.run();
}
});
list.add(t);
t.start();
}
}
public void submit(Runnable runnable) throws InterruptedException {
blockingDeque.put(runnable);
}
}
public class pool {
public static void main(String[] args) throws InterruptedException {
MyPool myPool=new MyPool(5);
for(int i=0;i<1000;i++){
int n=i;
//因为这里是变量捕获语法变量是事实不可变的或final修饰这里可以采取每次都重新定义一个新的变量n(小技巧)
myPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("当前线程是"+Thread.currentThread().getName()+"完成任务为"+n);
}
});
}
}
}
今天的分享到这里就结束了,感谢大家支持