多线程之线程池

线程池

线程池主要的知识点:

  1. 三大方法:
  2. 七大参数
  3. 四打拒绝策略

1.三大方法

线程池中的三大方法在Executors的工具类中

  1. Executors.newSingleThreadExecutor();//单一的线程池,单个线程
  2. Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
  3. Executors.newCachedThreadPool();//创建一个可扩缩的线程池

单一线程池测试

public class ThreadPoolDemo01 {
    public static void main(String[] args) {
        //1.线程池三大方法
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池

        try {
            for (int i = 0; i < 5; i++) {
                threadPool1.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadPool1.shutdown();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUpjeaNJ-1663857307379)(img_1.png)]

单一线程池只有一个线程工作

固定大小的线程池

public class ThreadPoolDemo01 {
    public static void main(String[] args) {
        //1.线程池三大方法
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池

        try {
            for (int i = 0; i < 10; i++) {
                threadPool2.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadPool2.shutdown();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BG5Qb31r-1663857307380)(img_2.png)]

固定大小线程池时最多有5个线程执行,即最高同时可以有5个并发

可扩缩的线程池测试

public class ThreadPoolDemo01 {
    public static void main(String[] args) {
        //1.线程池三大方法
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池

        try {
            for (int i = 0; i < 1000; i++) {
                threadPool3.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadPool3.shutdown();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TXBIvMfV-1663857307380)(img_3.png)]

可扩缩的线程池,可以根据线程的多少自动调整线程池大小

2.七大参数

Exxcutors工具类中创建线程池的方法实际是重载的ThreadPoolExecutor构造方法,所谓的七大参数就是该构造方法的参数而已。

  public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

直接来看测试类

public class ThreadPoolDemo02 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,//核心线程池大小
                5,//最大核心线程池大小
                2,//超时时间,超时后,若没有被使用就会释放线程池中除了核心的其他
                TimeUnit.SECONDS,//超时单位
                new LinkedBlockingDeque<>(3),//阻塞队列
                Executors.defaultThreadFactory(),//线程工厂,创建线程的
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );
    }
}

这几个参数的理解,如上面测试方法所示

  1. 当线程数>=5时,只有核心线程池会工作,即第一个参数
  2. 当线程数>5时,第五个参数是一个规定大小的阻塞队列,此时阻塞队列中会有等待的线程,此时才会额外的开放线程池,但不会超过最大核心线程池即第二个参数
  3. 当线程数>8时,阻塞队列中线程数满了,会触发拒绝策略。即第七个参数
  4. 另外,第三个参数时超时时间,第四个参数超时时间单位,超过对应时间,若没有线程使用线程池,则会释放线程池。第六个参数是创建线程的线程工厂,一般使用默认的。

3.四大拒绝策略

所谓拒绝策略就是在阻塞队列满了之后,线程池对没有进入线程池的线程的四种处理。

  1. AbortPolicy:第一种拒绝策略:超过最大可处理线程数后抛异常;
  2. DiscardPolicy:第二种拒绝策略:超过最大可处理线程数后报错不抛异常
  3. CallerRunsPolicy:第三种拒绝策略:超过最大可处理线程数后,将超出线程退回并交给上层线程处理(即调用者线程去处理)
  4. DiscardOldestPolicy()//第四种拒绝策略:超过最大可处理线程数后,不抛异常,但是后进入的线程会尝试调用最先进入的线程,如果最先进入的线程已经执行完毕,则调用此线程.
public class ThreadPoolDemo03 {
    public static void main(String[] args) {
        ExecutorService threadPool1 = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()//第一种拒绝策略:超过最大可处理线程数后抛异常
        );

        ExecutorService threadPool2 = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()//第二种拒绝策略:超过最大可处理线程数后报错不抛异常
        );

        ExecutorService threadPool3 = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()//第三种拒绝策略:超过最大可处理线程数后,将超出线程退回并交给上层线程处理(即调用者线程去处理)
        );

        ExecutorService threadPool4 = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()//第四种拒绝策略:超过最大可处理线程数后,不抛异常,但是后进入的线程会尝试调用最先进入的线程,如果最先进入的线程已经执行完毕,则调用此线程
        );
        for (int i = 0; i < 10000; i++) {
            threadPool4.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
        }
    }
}

4.如何创建线程池

  1. CPU密集型:cpu内核数量是最大线程池大小。
  2. IO密集型:判断程序中十分耗时的IO线程数,最大线程池大小 > 此线程数
public class ThreadPoolDemo04 {
    public static void main(String[] args) {
        //获取cpu内核数量
        System.out.println(Runtime.getRuntime().availableProcessors());
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                Runtime.getRuntime().availableProcessors(),
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}

5.总结

线程池的本质是池化技术,所谓池化技术,是程序在运行时会占用资源,为了优化资源的使用使用池化技术。比如JDBC连接池,
由于和数据库建立连接和关闭时比较消耗资源,所以事先准备好资源。

线程池的优点:降低资源消耗,提高响应速度,而且方便管理,最重要的是线程服用,可以控制并发数量。

你可能感兴趣的:(并发编程,java,jvm,算法)