您的位置:

手动创建线程池

一、什么是线程池?

线程池是一种线程的集合,线程池中包含了多个线程,这些线程可以轮流执行池中的任务,从而实现线程的复用,提高系统的效率,避免资源的浪费。线程池具有以下特点:

1、线程可以轮流执行,避免创建或销毁线程的开销

2、线程数量可以控制,防止系统因为线程过多而崩溃

3、线程池中的线程可以自动重用,不需要每次都重新创建线程

4、线程池可以根据不同的任务进行优先级排序,保证高优先级任务先执行

二、创建线程池的步骤

创建线程池的步骤如下:

1、创建一个线程池对象,设置线程池的属性,如核心线程数量、最大线程数量、线程空闲时间、等待队列长度、拒绝策略等等

2、创建一个等待队列,用于存放等待执行的任务

3、创建一个线程池管理器,用于管理线程池的状态,比如线程的创建、销毁、执行任务等

4、向线程池中添加任务,线程池会自动分配线程去执行任务

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPool {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //添加任务到线程池
        for(int i=0;i<10;i++){
            final int task = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行任务:"+task);
                }
            });
        }
        //关闭线程池
        executorService.shutdown();
    }
}

三、常见的线程池类型

Java中常见的线程池类型有如下几种:

1、FixedThreadPool:固定数量线程池,线程数量一旦设定就不会发生变化。

2、CachedThreadPool:缓存线程池,线程数量可以根据任务的多少自动调整。

3、SingleThreadExecutor:单线程线程池,只有一个线程在执行任务,适合于重要的任务需要顺序执行的场景。

4、ScheduledThreadPool:定时任务线程池,适用于需要按照一定的周期执行任务的场景。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MyScheduledThreadPool {
    public static void main(String[] args) {
        //创建定时任务线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        //延迟1秒后执行任务
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行任务");
            }
        },1, TimeUnit.SECONDS);
        //每隔2秒执行一次任务
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行任务");
            }
        }, 0, 2, TimeUnit.SECONDS);
        //关闭线程池
        scheduledExecutorService.shutdown();
    }
}

四、线程池的拒绝策略

当线程池中的线程已经全部被占用,等待队列已经满了,此时如果还有新的任务进来,线程池就需要执行拒绝策略。Java中提供了四种线程池的拒绝策略。

1、AbortPolicy:拒绝新任务并抛出异常

2、CallerRunsPolicy:将新任务提交给调用线程来执行

3、DiscardOldestPolicy:将等待队列中最早的任务丢弃,将新任务添加到等待队列尾部

4、DiscardPolicy:直接丢弃新任务

import java.util.concurrent.*;

public class MyRejectedThreadPool {
    static class MyTask implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"执行任务");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        //创建线程池,设置拒绝策略为DiscardPolicy
        ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
        //添加任务到线程池
        for(int i=0;i<5;i++){
            executorService.execute(new MyTask());
        }
        //关闭线程池
        executorService.shutdown();
    }
}

五、线程池的最佳实践

1、尽量设置合适的线程数量

线程数量过多会导致系统开销过大,过少会导致系统无法充分利用资源。

2、合理的等待队列长度

等待队列长度过长会导致系统处理速度变慢,过短会导致任务不能得到处理。

3、使用合适的拒绝策略

不同的应用场景需要使用不同的拒绝策略。

4、合理的任务划分与设计

任务应该尽可能合理的拆分,并根据优先级和执行时间进行排序。

六、总结

线程池是一种用于管理线程的机制,具有复用线程、控制线程数量、自动重用、以及优先级等特点,可以提高系统效率,避免资源浪费。在使用线程池时,要注意线程池的属性设置、拒绝策略的选择、以及任务的合理划分与设计,从而达到最佳的效果。