Java并发编程十线程池工具类

10 篇文章 1 订阅


Java并发编程一:并发基础必知
Java并发编程二:Java中线程
Java并发编程三:volatile使用
Java并发编程四:synchronized和lock
Java并发编程五:Atomic原子类
Java并发编程六:并发队列
Java并发编程七:ReentrantReadWriteLock和StampedLock
Java并发编程八:CountDownLatch、CyclicBarrier、Semaphore和Exchanger
Java并发编程九:线程池
Jdk中Executors为我们提供了七种常用线程池工具类,分别是:

  1. newFixedThreadPool:创建一个固定数目的、可重用的线程池。
  2. newCachedThreadPool:创建一个可缓存线程池,可灵活回收线程。
  3. newScheduledThreadPool:创建一个定时线程池,支持周期性的任务执行。
  4. newSingleThreadExecutor:创建一个单线程化的线程池,唯一的工作线程来执行任务,任务按照制定顺序(FIFO、LIFO、优先级)来执行。
  5. newSingleThreadScheduledExecutor::创建一个单线程化定时线程池,定期或延时执行任务。
  6. newWorkStealingPool:并行级别的工作窃取模式的线程池,默认是大小CPU的核数。(1.8)
  7. ForkJoinPool:支持大任务分解成小任务的线程池,通常配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。(1.8)

newFixedThreadPool

根据提交的任务是否创建线程,如果核心线程未满,则创建线程执行,不满则放置队列中。

public class Test {
    public static void main(String[] args) {
        ExecutorService executorService= Executors.newFixedThreadPool(5);
       for (int i = 1; i <11 ; i++) {
            int count = i;
            executorService.execute(()->{
               System.out.println(Thread.currentThread().getName()+"执行了任务"+ count);
            });
        }
         executorService.shutdown();
    }
}

运行结果:

pool-1-thread-1执行了任务1
pool-1-thread-5执行了任务5
pool-1-thread-4执行了任务4
pool-1-thread-3执行了任务3
pool-1-thread-2执行了任务2
pool-1-thread-1执行了任务6
pool-1-thread-4执行了任务8
pool-1-thread-2执行了任务9
pool-1-thread-5执行了任务7
pool-1-thread-3执行了任务10

newCachedThreadPool

提交的任务如果没有空闲线程则新建,如果有复用空闲线程,空间线程默认60秒被回收。

public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 1; i <11 ; i++) {
            int count = i;
            //Thread.sleep(100*count);
            executorService.execute(()->{
               System.out.println(Thread.currentThread().getName()+"执行了任务"+ count);
            });
        }   
        executorService.shutdown();
    }
}

运行结果:

pool-1-thread-1执行了任务1
pool-1-thread-4执行了任务4
pool-1-thread-2执行了任务2
pool-1-thread-3执行了任务3
pool-1-thread-5执行了任务5
pool-1-thread-6执行了任务6
pool-1-thread-7执行了任务7
pool-1-thread-8执行了任务8
pool-1-thread-9执行了任务9
pool-1-thread-10执行了任务10

解开Thread.sleep(100*count)执行结果如下:

pool-1-thread-1执行了任务1
pool-1-thread-1执行了任务2
pool-1-thread-1执行了任务3
pool-1-thread-1执行了任务4
pool-1-thread-1执行了任务5
pool-1-thread-1执行了任务6
pool-1-thread-1执行了任务7
pool-1-thread-1执行了任务8
pool-1-thread-1执行了任务9
pool-1-thread-1执行了任务10

newScheduledThreadPool

周期性的执行任务,有三种方式来执行。

  1. schedule为多长时间后执行任务,仅执行一次。
  2. scheduleAtFixedRate多长时间后执行任务,然后按照周期不停的去执行。
  3. scheduleWithFixedDelay跟scheduleAtFixedRate类似,不同在于:前者是任务执行完后再隔多长时间执行,而后者不管执行是否结束,到达时间点就执行。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
        System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":newScheduledThreadPool 开始....");
        executorService.schedule(()->{
            System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":schedule 3 秒后运行");
        },3, TimeUnit.SECONDS);
        executorService.scheduleAtFixedRate(()->{
            try {
                System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":scheduleAtFixedRate 5 秒后运行 2秒运行一次");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },5,2,TimeUnit.SECONDS);
        executorService.scheduleWithFixedDelay(()->{
            try {
                System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":scheduleAtFixedRate 10 秒后运行 2秒运行一次");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },5,2,TimeUnit.SECONDS);

运行结果:

2019-06-26 02:26:57:newScheduledThreadPool 开始…
2019-06-26 02:27:00:schedule 3 秒后运行
2019-06-26 02:27:02:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:02:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:07:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:09:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:12:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:16:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:17:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:22:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:23:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:27:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:30:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:32:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:37:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:37:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:42:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:44:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:47:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:51:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:52:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:57:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:58:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:28:02:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:28:05:scheduleAtFixedRate 10 秒后运行 2秒运行一次

newWorkStealingPool

会创建一个含有足够多线程的线程池,来维持相应的并行级别,它是一种工作窃取模式,每个处理器的核都对应着处理的任务,当一个核的任务处理完后,会帮助其他核来处理任务。

        // 默认为系统的核数
        ExecutorService executorService = Executors.newWorkStealingPool(4);
        for (int i = 0; i < 10; i++) {
            int count = i;
            executorService.execute(()->{
                try {
                    Thread.sleep(count *100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            });
        }
        // WorkStealingPool是一种后台线程,要不然看不到运行结果。
          System.in.read();

运行结果:

ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-2

通过结果可以看出,当线程1执行完毕后,它会在帮忙再次执行其他任务。

ForkJoinPool

在并发编程中,我们经常遇见把大任务拆分成小任务来执行,ForkJoinPool是一种支持任务分解的线程池,按照预先设置好的规则来拆分任务,最后合并。一般配合分配任务接口ForkJoinTask来使用,ForkJoinTask有两个实现类:RecursiveAction和RecursiveTask。区别在于前者没有返回值,后者有返回值。

 // 最大任务量
    private static final int MAX_NUMBER = 1000000;
    private static int[] nums = new int[5000000];
    static {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = new Random().nextInt(100)+1;
        }
    }
    public static void main(String[] args) throws InterruptedException,ExecutionException {
        System.out.println("结果为:"+ Arrays.stream(nums).sum());
        // 默认为系统的核数
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        MyTask myTask = new MyTask(0, nums.length);
        forkJoinPool.submit(myTask);
        System.out.println("结果为:"+myTask.get());
    }
    static class MyTask extends RecursiveTask<Long>{

        private int start;
        private int end;

        public MyTask(int start, int end){
               this.start = start;
               this.end = end;
        }

        @Override
        protected Long compute() {
            if (end - start < MAX_NUMBER){
                // 不用再分任务
                long result = 0;
                for (int i = start; i < end; i++) {
                    result = result + nums[i];
                }
                System.out.println("from:"+start+"---to:"+end+"----="+result);
                return result;
            }else {
                // 继续切分任务
                int middle = start + (end - start)/2 ;
                MyTask leftTask = new MyTask(start, middle);
                MyTask rightTask = new MyTask(middle, end);
                // 执行任务
                leftTask.fork();
                rightTask.fork();
                return leftTask.join()+rightTask.join();
            }
        }
    }

运行结果:

结果为:252400222
from:2500000—to:3125000----=31505184
from:1875000—to:2500000----=31552670
from:3125000—to:3750000----=31623470
from:0—to:625000----=31505421
from:3750000—to:4375000----=31531536
from:1250000—to:1875000----=31554030
from:625000—to:1250000----=31544988
from:4375000—to:5000000----=31582923
结果为:252400222

注意:ForkJoinPool在执行过程中,会创建大量的子任务,导致GC进行垃圾回收。

使用说明

在这里插入图片描述
阿里的解释足够权威了吧,自己做一个线程池。

 ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), new MyThreadFactory(), (r, executor1) -> System.out.println( executor1.toString()));
        for (int i = 0; i < 10; i++) {
            executor.execute(()->{
                try {
                    Thread.sleep((new Random().nextInt(100)+1)*100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread()+"执行了");
                System.out.println( executor.toString());
            });
        }
    }
     static class MyThreadFactory implements ThreadFactory {
       private final AtomicInteger mThreadNumber = new AtomicInteger(0);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r,"my-thread-" + mThreadNumber.incrementAndGet());
        }
    }

运行结果:

java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 4, completed tasks = 1]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 3, completed tasks = 2]
Thread[my-thread-2,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 3]
Thread[my-thread-1,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 1, completed tasks = 4]
Thread[my-thread-2,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 5]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 6]
Thread[my-thread-1,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 1, queued tasks = 0, completed tasks = 7]

至于参数和结果问题可以查看上一章。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值