【java多线程学习之四】线程池原理以及获取线程结果的三种方式

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

1 线程池的工作原理以及7个参数

线程池的工作原理和7个参数(京东物流、美团充电宝一面、中信银行一面、美团外卖一面、58用户平台一面、轻松集团)

1.1 7个参数

  • corePoolSize:线程池核心线程数量
  • maximumPoolSize:线程池最大线程数量
  • keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
  • unit:存活时间的单位
  • workQueue:存放任务的队列
  • threadFactory:创建线程的工厂
  • handler:超出线程范围和队列容量的任务的处理程序 每个值为什么这么设置?

1.2 工作原理

例子: 核心线程数量为5个;全部线程数量为10个;工作队列的长度为5。

刚开始都是在创建新的线程,达到核心线程数量5个后,新的任务进来后不再创建新的线程,而是将任务加入工作队列;

任务队列到达上线5个后,新的任务又会创建新的普通线程,直到达到线程池最大的线程数量10个;

后面的任务则根据配置的饱和策略来处理。我们这里没有具体配置,使用的是默认的配置AbortPolicy:直接抛出异常。

当当前任务小于最大线程数的时候,线程资源会保持核心线程池个数的线程,其他超过的线程资源在存活时间时间之后会被回收。

1.3 拒绝策略

1、AbortPolicy:直接抛出异常 2、CallerRunsPolicy:只用调用所在的线程运行任务 3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 4、DiscardPolicy:不处理,丢弃掉。

1.4 代码

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * 线程池测试
 * 使用丢弃策略处理多余线程,不管多余线程
 */
public class ThreadPoolTest2 {
	public static void main(String[] args) {
		LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(5);
		// 丢弃策略,多余的线程直接不处理,丢弃
		RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
		// 创建线程池 5 10 60s
		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60,
				TimeUnit.SECONDS, queue, handler);
		for (int i = 1; i <= 16; i++) {
			threadPool.execute(new Thread(new ThreadTest(), "Thread".concat(i + "")));
			System.out.println("线程池中活跃的线程数: " + threadPool.getPoolSize());
			if (queue.size() > 0) {
				System.out.println("-------队列中阻塞的线程数" + queue.size());
			}
		}
		threadPool.shutdown();
	}

}

输出:

com.geniu.concurrent.ThreadPool.ThreadPoolTest2
线程池中活跃的线程数: 1
线程池中活跃的线程数: 2
线程池中活跃的线程数: 3
线程池中活跃的线程数: 4
线程池中活跃的线程数: 5
线程池中活跃的线程数: 5
-------队列中阻塞的线程数1
线程池中活跃的线程数: 5
-------队列中阻塞的线程数2
线程池中活跃的线程数: 5
-------队列中阻塞的线程数3
线程池中活跃的线程数: 5
-------队列中阻塞的线程数4
线程池中活跃的线程数: 5
-------队列中阻塞的线程数5
线程池中活跃的线程数: 6
-------队列中阻塞的线程数5
线程池中活跃的线程数: 7
-------队列中阻塞的线程数5
线程池中活跃的线程数: 8
-------队列中阻塞的线程数5
线程池中活跃的线程数: 9
-------队列中阻塞的线程数5
线程池中活跃的线程数: 10
-------队列中阻塞的线程数5
线程池中活跃的线程数: 10
-------队列中阻塞的线程数5

源码地址:github.com/zhongsb/Jav… 参考:www.cnblogs.com/dongguacai/…

2 三种获取线程执行结果的方法

2.1、Callable 线程

public class FetchAdTask implements Callable<Ad> {

    @Override
    public Ad call() throws Exception {
        System.out.println("fetch task");
        sleep(1000L);
        return null;
    }
}

2.2、使用Future,包括 FutureTask、CompletableFuture

CompletableFuture.get();

Future 的优点:可以对任务设置时限,如果超时了,可以取消,然后返回一个默认结果,防止 某一个任务出现问题,导致系统出现问题。

f.get(timeLeft, TimeUnit.NANOSECONDS);

或者通过 invokeAll() 返回限定时间范围内的所有任务的结果。

executor.invokeAll(tasks, time, unit);   

CompletableFuture, 使用 supplyAsync 方法提交线程,使用 get 方法获取结果。

CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {
        System.out.println("任务3, 线程名字" + Thread.currentThread().getName());
        try {
            sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 3;
    });

    CompletableFuture.allOf(task1, task2, task3, task4);
    System.out.println("end: " + new Date());
    task1.get();

2.3、使用 CompletionService,

CompletionService.take();

例子

private static final long TIME_BUDGET = 100L;
private static final Ad DEFAULT_AD = new Ad();

private final ExecutorService executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1,
        Runtime.getRuntime().availableProcessors() + 1, 0L,
        TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1000));


public static void main(String[] args) {
    try {
        Test616LimitedTimeTask task = new Test616LimitedTimeTask();
        task.test();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

/**
 * 测试超时取消
 *
 * @throws InterruptedException
 */
void test() throws InterruptedException {
    Ad ad = null;
    long endNanos = System.nanoTime() + TIME_BUDGET;
    Future<Ad> f = executor.submit(new FetchAdTask());
    try {
        long timeLeft = endNanos - System.nanoTime();
        // 增加参数 超时时间和超时时间的单位
        ad = f.get(timeLeft, TimeUnit.NANOSECONDS);
    } catch (ExecutionException e) {
        ad = DEFAULT_AD;
    } catch (TimeoutException e) {
        // 超时,取消任务
        ad = DEFAULT_AD;
        System.out.println("超时取消");
        f.cancel(true);
    }
}    

优点:多个 CompletionService 可以共享一个 Executor,因此可以创建一个对于特定计算私有, 又能共享一个公共 Executor 的 ExecutorCompletionService。

源码:github.com/zhongsb/Jav…

posted @ 2021-08-07 22:05  六七十三  阅读(54)  评论(0编辑  收藏  举报  来源