多线程与高并发day09

本文介绍了Java线程池的核心概念,包括ExecutorService、Callable接口和Future,详细讲解了ThreadPoolExecutor的参数及工作原理,并讨论了默认的拒绝策略及其应用场景。通过示例代码展示了线程池的使用,如SingleThreadPool、CachedThreadPool、FixedThreadPool和ScheduledThreadPool,以及如何自定义拒绝策略。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

个人学习笔记,仅供参考!欢迎指正!


一、线程池几个常用类的拓展

1.Excutor

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     */
    void execute(Runnable command);
}

ExcutorService继承自Excutor,此外还完善了任务执行器的整个生命周期。线程池的实现在ExcutorService的基础之上。

public interface ExecutorService extends Executor {
    /**
     * Initiates an orderly shutdown in which previously submitted
     * tasks are executed, but no new tasks will be accepted.
     * Invocation has no additional effect if already shut down.
     *
     * <p>This method does not wait for previously submitted tasks to
     * complete execution.  Use {@link #awaitTermination awaitTermination}
     * to do that.
     *
     */
    void shutdown();
    /**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution.
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     * @return list of tasks that never commenced execution
     */
    List<Runnable> shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * Submits a value-returning task for execution and returns a
     * Future representing the pending results of the task. The
     * Future's {@code get} method will return the task's result upon
     * successful completion.
     */
    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);
    ......
    ......
}

2.Callable接口

此接口与Runnable相似,可以交由线程处理,但有返回结果。

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code call}
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

认识Callable,对Runnable进行了扩展, 对Callable的调用,可以有返回值:

public class Day08 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> c = new Callable() {
            @Override
            public String call() throws Exception {
                return "Hello Callable";
            }
        };
        ExecutorService service = Executors.newCachedThreadPool();
        Future<String> future = service.submit(c); //异步
        System.out.println(future.get());//阻塞
        service.shutdown();
    }
}

3.Future

存储执行的将来才会产生的结果:
FutureTask -> Future+Runnable

CompletableFuture:可以管理多个Future的结果,可以对任务可以进行各种各样的组合,所有的任务完成以后执行什么结果?以及任何一个任务完成后要执行什么结果?等等:

public class Day08 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start, end;
        
        start = System.currentTimeMillis();

        CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(()->priceOfTM());
        CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(()->priceOfTB());
        CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(()->priceOfJD());

        CompletableFuture.allOf(futureTM, futureTB, futureJD).join();

        CompletableFuture.supplyAsync(()->priceOfTM())
                .thenApply(String::valueOf)
                .thenApply(str-> "price " + str)
                .thenAccept(System.out::println);
        
        end = System.currentTimeMillis();
        System.out.println("use completable future! " + (end - start));

        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static double priceOfTM() {
        delay();
        return 1.00;
    }
    private static double priceOfTB() {
        delay();
        return 2.00;
    }
    private static double priceOfJD() {
        delay();
        return 3.00;
    }
    private static void delay() {
        int time = new Random().nextInt(500);
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("After %s sleep!\n", time);
    }
}

二、线程池

1.ThreadPoolExecutor

ThreadPoolExcutor继承自AbstractExcutorService。阿里手册中规定线程池都需要是自定义的:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

手动定义一个线程池,代码示例如下:

public class T05_00_HelloThreadPool {

    static class Task implements Runnable {
        private int i;

        public Task(int i) {
            this.i = i;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Task " + i);
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public String toString() {
            return "Task{" +
                    "i=" + i +
                    '}';
        }
    }
    public static void main(String[] args) {
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 8; i++) {
            tpe.execute(new Task(i));
        }

        System.out.println(tpe.getQueue());

        tpe.execute(new Task(100));

        System.out.println(tpe.getQueue());

        tpe.shutdown();
    }
}

线程池内部维护者两个集合,一个存放线程另一个存放任务:在逻辑上可以按下图理解(其实内部维护的是引用)
在这里插入图片描述

线程池的7个参数:

  1. int corePoolSize 核心线程数,核心线程一般不参与归还操作系统(空闲时间超过生存时间)

  2. int maximumPoolSize 最大线程数, 当需要额外线程时最多能启用到多少个线程包括核心线程

  3. long keepAliveTime 生存时间, 线程长时间处于空闲状态(以生存时间为准),则归还于系统

  4. TimeUnit unit 生存时间的单位, 可以是秒、毫秒等

  5. BlockingQueue workQueue 任务队列

  6. ThreadFactory threadFactory 线程工厂:

在这里插入图片描述

只有一个方法,就是创建线程,参数必须是实现此接口的。
例如:Executors.defaultThreadFactory()
在这里插入图片描述默认的defaultThreadFactory是一个实现ThreadFactory的工厂类:

    /**
     * The default thread factory
     */
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),//指定线程名称
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);//指定线程为非守护线程
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);//设定线程的优先级为普通优先级
            return t;
        }
    }

也可以自己定义一个,实现ThreadFactory接口的工厂,来产生以及愿意的线程。

  1. RejectedExecutionHandler handler 拒绝策略:当任务队列满了、而所有的线程(已达到最大线程)都在忙,当任务再次添加来到时执行拒绝策略。JDK默认提供四种拒绝策略,当然也可以自定义。
    在这里插入图片描述

2.默认拒绝策略应用场景

使用DiscardOldest:可以看到Task{i=2}被扔掉了。
在这里插入图片描述使用Abort:抛异常
在这里插入图片描述
使用DiscardPolicy:不抛异常,但不执行

使用CallerRun: 由main线程调度所以由main线程执行
在这里插入图片描述

一般都是自定义拒绝策略,例如保存信息到Kfaka、MQ等,做好日志再来处理,当有大量日志中任务未处理则说明,该加机器提升性能了。

Excutors可以视为线程池的工厂,用来生产各种各样的线程池。

3.JDK提供的默认实现

SingleThreadPool:线程池中只有一个线程,可以保证扔进去的任务是顺序执行的。

public class T07_SingleThreadPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        for(int i=0; i<5; i++) {
            final int j = i;
            service.execute(()->{
                System.out.println(j + " " + Thread.currentThread().getName());
            });
        }
    }
}

为什么要有单线程的线程池? 线程池是有任务队列的,有完整的生命周期管理等。
在这里插入图片描述

CachedThreadPool:核心线程数为零、最大线程数为int类型最大数值,使用SynchronousQueue队列,工厂和拒绝策略都使用默认的。线程不闲时,基本来一个任务起一个线程(SynchronousQueue是一个手递手容量为空的Queue来一个任务必须有个线程给拿走,不然提交任务的线程就阻塞住了)特点为:来一个任务必须马上执行。
在这里插入图片描述

示例代码:

public class T08_CachedPool {
	public static void main(String[] args) throws InterruptedException {
		ExecutorService service = Executors.newCachedThreadPool();
		System.out.println(service);
		for (int i = 0; i < 2; i++) {
			service.execute(() -> {
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName());
			});
		}
		System.out.println(service);
		TimeUnit.SECONDS.sleep(80);
		System.out.println(service);
	}
}

FixedThreadPool:线程数固定
在这里插入图片描述
如何选择场景:当任务峰值波动大时可使用Cached、当比较平稳知道需要多少线程可以hold住时使用Fixed。
在这里插入图片描述
在这里插入图片描述
代码示例如下:

public class T09_FixedThreadPool {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        getPrime(1, 200000);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        final int cpuCoreNum = 4;

        ExecutorService service = Executors.newFixedThreadPool(cpuCoreNum);

        MyTask t1 = new MyTask(1, 80000); //1-5 5-10 10-15 15-20
        MyTask t2 = new MyTask(80001, 130000);
        MyTask t3 = new MyTask(130001, 170000);
        MyTask t4 = new MyTask(170001, 200000);

        Future<List<Integer>> f1 = service.submit(t1);
        Future<List<Integer>> f2 = service.submit(t2);
        Future<List<Integer>> f3 = service.submit(t3);
        Future<List<Integer>> f4 = service.submit(t4);

        start = System.currentTimeMillis();
        f1.get();
        f2.get();
        f3.get();
        f4.get();
        end = System.currentTimeMillis();
        System.out.println(end - start);
    }
    static class MyTask implements Callable<List<Integer>> {
        int startPos, endPos;

        MyTask(int s, int e) {
            this.startPos = s;
            this.endPos = e;
        }
        @Override
        public List<Integer> call() throws Exception {
            List<Integer> r = getPrime(startPos, endPos);
            return r;
        }
    }
    static boolean isPrime(int num) {
        for(int i=2; i<=num/2; i++) {
            if(num % i == 0) return false;
        }
        return true;
    }
    static List<Integer> getPrime(int start, int end) {
        List<Integer> results = new ArrayList<>();
        for(int i=start; i<=end; i++) {
            if(isPrime(i)) results.add(i);
        }
        return results;
    }
}

并行和并发的对比:concurrent vs parallel
并行是并发的子集,并发指任务提交,并行指任务的执行,FixedThreadPool是可以让任务并行执行的。

ScheduledThreadPool:定时任务线程池,可以用来执行定时任务。DelayedWorkQueue 可以指定隔多长时间后运行。
在这里插入图片描述
代码示例如下:

public class T10_ScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
        service.scheduleAtFixedRate(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }, 0, 500, TimeUnit.MILLISECONDS);
    }
}

4.手敲一个定义拒绝策略的例子

在这里插入图片描述

需要实现RejectedExecutionHandler接口,代码示例如下:

public class T14_MyRejectedHandler {
    public static void main(String[] args) {
        ExecutorService service = new ThreadPoolExecutor(4, 4,
                0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(6),
                Executors.defaultThreadFactory(),
                new MyHandler());
    }

    static class MyHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //log("r rejected")
            //save r kafka mysql redis
            //try 3 times
            if(executor.getQueue().size() < 10000) {
                //try put again();
            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值