Java线程池

 1 Executo

服务端项目,经常需要处理执行时间很短而数目巨大的请求,如果为每一个请求创建一个新的线程,则会导致性能上的瓶颈。线程池核心原理是对线程对象进行管理,包括创建与销毁。

图 线程池相关UML图

1.1 Executors

ThreadPoolExecutor在实例化时需传入多个参数,还要考虑线程的并发数等与线程池运行效率有关的参数,所以官方建议使用Executors工厂类来创建线程池对象。

图 Executors类中的方法

1.1.1 newCachedThreadPool()

创建无界线程池。

public class UnboundedThreadPool {

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":" + System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for(int i = 0; i < 3; i++) {
            executorService.execute(new MyRunnable());
        }
        TimeUnit.SECONDS.sleep(3);
        for (int i = 0; i < 3; i++) {
            executorService.execute(new MyRunnable());
        }
    }
}
/*
运行结果(程序还未终止):
pool-1-thread-3:1679829983418
pool-1-thread-1:1679829983417
pool-1-thread-2:1679829983418
pool-1-thread-1:1679829986430
pool-1-thread-2:1679829986431
pool-1-thread-3:1679829986432
 */

在线程池中,线程对象是可以复用的。

但是该方法创建无界线程池极易造成内存占用率大幅升高,导致内存溢出或者系统运行效率严重下降。

public class UnboundedThreadPool2 {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 3000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
}

图 无界线程池创建了超多的线程

1.1.2 newFixedThreadPool(int)

创建有界线程池,池中线程个数可以指定最大数量。

public class BoundedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 3000; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}

图 有界线程池最多创建5个线程

2 ThreadPoolExecutor

2.1 构造方法

图 ThreadPoolExecutor构造方法

corePoolSize: 池中至少要保留的线程数,定义corePool核心池的大小

maximumPoolSize:池中运行的最大线程数。

keepAliveTime:当线程数量大于corePoolSize值时,在没有超过指定的时间内是不能从线程池中将空闲线程删除,如果超过此时间,则删除空闲线程。能删除的范围是corePool之外的线程。

unit: keepAliveTime参数的时间单位

workQueue:执行前用于保持任务的队列

threadFactory:线程工厂

handler: 池中的资源被全部占用时,对新添加的任务的处理策略

2.1.1 构造函数详解

核心线程数:C,运行最大线程数:M,要执行任务的线程:N。指定时间:T

(1)使用无参new LinkedBlockingQueue():特点是只使用核心池的线程执行任务。M,T参数设置无效。

N <= C

立即在核心池中创建线程并运行,这些任务不会被放入LinkedBlockingQueue中。

N > C

会把N-C部份放入LinkedBlockingQueue中等待核心池的线程执行。

表 无参LinkedBlockingQueue下不同任务数量的任务执行逻辑

(2)使用SynchronousQueue:不会把任务放入该队列中。

N <= C

立即在核心线程池中创建线程并运行任务。

N > C && N <= M

马上创建最多M个线程运行任务,N- C执行完任务后在超过T时,将N- C部份清除,如果N- C在T内未执行完任务,则在其完成任务后进行清除。

N > M

最多处理M个任务,其他任务不再处理并抛出异常。

表 SynchronousQueue下不同任务数量的任务执行逻辑

(3)使用有参new LinkedBlockingQueue(X): X表示队列最大存储长度。特点是核心池中的线程和M- C线程可能一起执行任务。

N <= C

立即在核心池中创建线程并运行任务。

N > C && (N-C) <= X

立即在核心池中创建线程并运行任务,并把N- C放入队列中等待核心池中的线程执行。

N > C && (N-C) > X && N – C – X <= M - C

C + X 任务运行逻辑和上面一样。马上创建N – C – X个线程运行这些任务,清除逻辑和SynchronousQueue情况的一样。

N > C && N – C > X && N – C – X > M - C

多出来的任务被拒绝并出现异常。

表 有参LinkedBlockingQueue下不同任务数量的任务执行逻辑

public class LinedBlockingQueuePool {

    static final int CORE = 3, LINKED_SIZE = 2, MAX = 5;
    static final long TIME = 2; // 任务清除超时时间

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":" + System.currentTimeMillis());
            try {
                TimeUnit.SECONDS.sleep(TIME + 2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(LINKED_SIZE);
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(CORE, MAX, TIME, TimeUnit.SECONDS, blockingQueue);

        for(int i = 0; i < MAX + LINKED_SIZE + 3; i++) {
            poolExecutor.execute(new MyRunnable());
        }

    }
}
/*
运行结果:
pool-1-thread-3:1679843594419
pool-1-thread-2:1679843594418
pool-1-thread-1:1679843594419
pool-1-thread-4:1679843594419
pool-1-thread-5:1679843594419
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.huangmingfu.day7.article.LinedBlockingQueuePool$MyRunnable@12a3a380 rejected from java.util.concurrent.ThreadPoolExecutor@29453f44[Running, pool size = 5, active threads = 5, queued tasks = 2, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.huangmingfu.day7.article.LinedBlockingQueuePool.main(LinedBlockingQueuePool.java:29)
pool-1-thread-4:1679843598424
pool-1-thread-1:1679843598424
 */

2.1.2 拒绝策略

AbortPolicy

被拒绝时,抛出RejectedExecutionExeception异常,是线程池默认使用的拒绝策略。

CallerRunsPolicy

被拒绝时,会让调用者来处理被拒绝的任务。

DiscardOldestPolicy

被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。

DiscardPolicy

线程池将丢弃被拒绝的任务。

表 ThreadPoolExecutor类中的四种拒绝策略

public class CallerRunsPolicyPool {

    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(2);
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 4, 3, TimeUnit.SECONDS, linkedBlockingQueue, new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 10; i++) {
            poolExecutor.execute(() -> {
                System.out.println( "线程池中:" + Thread.currentThread().getName() + ":" + System.currentTimeMillis());
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}
/*
运行结果(程序未终止):
线程池中:pool-1-thread-1:1679844913803
线程池中:pool-1-thread-3:1679844913803
线程池中:pool-1-thread-2:1679844913803
线程池中:main:1679844913803
线程池中:pool-1-thread-4:1679844913803
线程池中:main:1679844918810
线程池中:pool-1-thread-3:1679844918810
线程池中:pool-1-thread-4:1679844918810
线程池中:pool-1-thread-3:1679844923813
线程池中:pool-1-thread-1:1679844923814
 */

2.2 shutdown()和shutdownNow()

shutdown():使当前未执行完成的任务继续执行,而队列中未执行的任务会继续执行,不删除队列中的任务,不再允许添加新的任务。该方法不会阻塞。

shutdownNow():使当前未执行完的任务继续执行,而队列中未执行的任务不再执行。删除队列中的任务,不再运行添加新的任务,该方法不会阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值