Java线程池

简介

线程池介绍

线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,对线程统一管理。

线程池就是存放线程的池子,池子里存放了很多可以复用的线程。

创建线程和销毁线程的花销是比较大的(手动new Thread 类),创建和消耗线程的时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程是比较消耗资源的。(我们可以把创建和销毁的线程的过程去掉)。

使用线程池的优势

提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
四种创建线程池的方式
Executors类(并发包)提供了4种创建线程池方法,这些方法最终都是通过配置ThreadPoolExecutor的不同参数,来达到不同的线程管理效果。

newCacheTreadPool

创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程

newFixedThread

创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。

newScheduleThreadPool

可以创建定长的、支持定时任务,周期任务执行。

newSingleExecutor

创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

newCacheTreadPool的创建与使用

创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程。
总结: 线程池的最大核心线程为无限大,当执行第二个任务时第一个任务已经完成,则会复用执行第一个任务的线程;如果第一个线程任务还没有完成则会新建一个线程。


public class ThreadPoolDemo {
    
    public static void main(String[] args) {

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        Runnable runnable = null;
        for (int i = 0; i < 5; i++) {
            runnable = ()->{
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName());
            };
            cachedThreadPool.execute(runnable);
        }
        
    }

}

image

newFixedThreadPool创建与使用

创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。
总结:创建指定长度的线程池,任务超出当前线程池执行线程数量则会一直等待,直到运行。

package com.mrs.interview.STread;

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

/**
 * 创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。
 * description: ThreadPoolWithFixedThreadPool
 * date: 2022/10/27 12:07
 * author: MR.孙
 */
public class ThreadPoolWithFixedThreadPool {

    public static void main(String[] args) {
        // 创建定长线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        Runnable runnable = null;
        for (int i = 0; i < 5; i++) {
           runnable = ()->{
               //实现Runnable接口中的run方法
               //创建任务
               try {
                   Thread.sleep(10);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               System.out.println(Thread.currentThread().getName());
           };
           //将任务交给线程池管理
            fixedThreadPool.execute(runnable);
        }


    }

}


image

newScheduledThreadPool

创建定长的、支持定时任务,周期任务执行
总结:以下案例中延迟2秒后开始执行线程池中的所有任务。

package com.mrs.interview.STread;


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

public class ThreadPoolWithScheduledThreadPool {
    //以下案例中延迟2秒后开始执行线程池中的所有任务
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        Runnable runnable = null;
        for (int i = 0; i < 5; i++) {
            runnable = ()->{
                System.out.println(Thread.currentThread().getName());
            };
            //将任务交给线程池管理, 延迟2秒后才开始执行线程中的所有任务
            scheduledThreadPool.schedule(runnable, 2, TimeUnit.SECONDS);

        }
    }

}

image

newSingleExecutor

创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

package com.mrs.interview.STread;

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

/**
 * description: ThreadPoolWithSingleThreadPool
 * date: 2022/10/27 12:29
 * author: MR.孙
 */
public class ThreadPoolWithSingleThreadPool {

    public static void main(String[] args) {
        // 创建单线程-线程池,任务依次执行
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        Runnable runnable = null;

        for (int i = 0; i < 5; i++) {
            //创建任务
            runnable = ()->{
                System.out.println(Thread.currentThread().getName());
            };
            //将任务交给线程池管理
            singleThreadExecutor.execute(runnable);

        }

    }

}
 

image

推荐多线程用法

推荐通过 new ThreadPoolExecutor() 的写法创建线程池,这样写线程数量更灵活,开发中多数用这个类创建线程。

ThreadPoolExecutor核心参数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

image

线程池的工作原理

image

多线程四种拒绝策略

这四种拒绝策略,在ThreadPoolExecutor是四个内部类。

AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
DiscardPolicy discardPolicy = new ThreadPoolExecutor.DiscardPolicy();
DiscardOldestPolicy discardOldestPolicy =
new ThreadPoolExecutor.DiscardOldestPolicy();
CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();

  1. AbortPolicy
    当任务添加到线程池中被拒绝时,直接丢弃任务,并抛出
    RejectedExecutionException异常。
  1. DiscardPolicy
    当任务添加到线程池中被拒绝时,丢弃被拒绝的任务,不抛异常。
  1. DiscardOldestPolicy
    当任务添加到线程池中被拒绝时,丢弃任务队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
  1. CallerRunsPolicy
    被拒绝任务的处理程序,直接在execute方法的调用线程中运行被拒绝的任务。
    总结:就是被拒绝的任务,直接在主线程中运行,不再进入线程池。

CallerRunsPolicy这种方式好多同学可能不太理解,这里给大家看个Demo:

package com.mrs.interview.STread;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * description: CallerRunsPolicyDemo
 * date: 2022/10/27 12:49
 * author: MR.孙
 */
public class CallerRunsPolicyDemo {

    public static void main(String[] args) {
        // 创建单线程-线程池, 任务依次执行
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 260, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2), new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 10; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            };
            threadPoolExecutor.execute(runnable);

        }
    }

}

image

被拒绝的任务直接在主线程中进行

posted @ 2022-10-27 15:15  长情c  阅读(116)  评论(0)    收藏  举报