Java 线程池多任务先完成先返回处理结果

范例1:线程池多任务先完成先返回处理结果

package com.contoso;

import java.util.Random;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CompletionServiceExample1 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Random random = new Random();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // CompletionService内部实现使用了blockingQueue<Future<V>>存储已经完成任务的执行结果
        CompletionService<String> service = new ExecutorCompletionService(executorService);

        for (int i = 1; i <= 10; i++) {
            final int index = i;
            service.submit(() -> {
                long start = System.nanoTime();
                Thread.sleep(random.nextInt(5000));
                System.out.println(Thread.currentThread().getName() + "|任务正在处理中 ...");
                long end = System.nanoTime();
                double timeTaken = (end - start) / 1e9;
                return String.valueOf(Thread.currentThread().getName() + " Task" + index + " Taken in seconds:" + timeTaken);
            });
        }

        for (int j = 1; j <= 10; j++) {
            long start = System.nanoTime();
            //没有完成的任务在这一行代码这里会出现阻塞,耗时最少任务的处理结果先从take里面出来
            Future<String> take = service.take();
            long end = System.nanoTime();
            double timeTaken = (end - start) / 1e9;
            System.out.println("主线程打印没有完成的任务被阻塞的时间(单位:秒)" + timeTaken);

            String result = take.get(); // 这一行代码在这里不会阻塞,引入放入队列中的都是已经完成的任务
            System.out.println("主线程打印返回任务的处理时间:" + result + "\n");
        }
        executorService.shutdown();
        System.out.println("所有任务已经完成,关闭线程池");
    }
}
run:
pool-1-thread-5|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.126341778
主线程打印返回任务的处理时间:pool-1-thread-5 Task5 Taken in seconds:0.12415651

pool-1-thread-1|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)1.199077737
主线程打印返回任务的处理时间:pool-1-thread-1 Task1 Taken in seconds:1.325494439

pool-1-thread-7|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)1.073619992
主线程打印返回任务的处理时间:pool-1-thread-7 Task7 Taken in seconds:2.39895914

pool-1-thread-6|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.027563512
主线程打印返回任务的处理时间:pool-1-thread-6 Task6 Taken in seconds:2.426902391

pool-1-thread-2|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.494719977
主线程打印返回任务的处理时间:pool-1-thread-2 Task2 Taken in seconds:2.921960165

pool-1-thread-4|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.538435259
主线程打印返回任务的处理时间:pool-1-thread-4 Task4 Taken in seconds:3.46030321

pool-1-thread-8|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.201699418
主线程打印返回任务的处理时间:pool-1-thread-8 Task8 Taken in seconds:3.661973811

pool-1-thread-3|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.012382544
主线程打印返回任务的处理时间:pool-1-thread-3 Task3 Taken in seconds:3.674586569

pool-1-thread-9|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.767456447
主线程打印返回任务的处理时间:pool-1-thread-9 Task9 Taken in seconds:4.442219117

pool-1-thread-10|任务正在处理中 ...
主线程打印没有完成的任务被阻塞的时间(单位:秒)0.015987835
主线程打印返回任务的处理时间:pool-1-thread-10 Task10 Taken in seconds:4.458432685

所有任务已经完成,关闭线程池
BUILD SUCCESSFUL (total time: 4 seconds)

范例2:线程池多任务先完成先返回处理结果(等价于范例1的效果,但是代码太复杂啦)

package com.contoso;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 
 * future.get()
 * future.get(long timeout, TimeUnit)
 */
public class ExecutorServiceExample2 {

    public static void main(String[] args) {
        int taskCount = 10;
        ExecutorService executor = Executors.newFixedThreadPool(taskCount);
        List<Future<String>> listFuture = new ArrayList();
        for (int i = 1; i <= taskCount; i++) {
            Future<String> future = executor.submit(new MyTask(5000, i));
            listFuture.add(future);
        }

        while (taskCount > 0) {
            for (Future<String> future : listFuture) {
                String data = null;
                try {
                    data = future.get(0, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    // 必须忽略超时异常,只要任务没有完成且等待时间为0就会报超时异常  
                    // e.printStackTrace();
                }

                if (data != null) {
                    System.out.println("主线程打印返回任务的处理时间:" + data); 
                    listFuture.remove(future);
                    taskCount--;                 
                    break;
                }
            }
        }

        executor.shutdown();
        System.out.println("所有任务已经完成,关闭线程池");
    }
}

class MyTask implements Callable<String> {

    private int sleep;
    private int indexTask;
    Random random = new Random();

    public MyTask(int sleep, int indexTask) {
        this.sleep = sleep;
        this.indexTask = indexTask;
    }

    @Override
    public String call() throws Exception {
        long start = System.nanoTime();
        Thread.sleep(random.nextInt(sleep));// 模拟任务的执行时间    
        String data = Thread.currentThread().getName() + " Task" + indexTask + " Taken in seconds:";
        long end = System.nanoTime();
        double timeTaken = (end - start) / 1e9;
        System.out.println(Thread.currentThread().getName() + "|任务已处理完成");
        return timeTaken + " " + data;
    }
}
run:
pool-1-thread-1|任务已处理完成
主线程打印返回任务的处理时间:0.009673773 pool-1-thread-1 Task1 Taken in seconds:
pool-1-thread-8|任务已处理完成
主线程打印返回任务的处理时间:1.051098125 pool-1-thread-8 Task8 Taken in seconds:
pool-1-thread-6|任务已处理完成
主线程打印返回任务的处理时间:1.24212383 pool-1-thread-6 Task6 Taken in seconds:
pool-1-thread-2|任务已处理完成
主线程打印返回任务的处理时间:1.404322572 pool-1-thread-2 Task2 Taken in seconds:
pool-1-thread-7|任务已处理完成
主线程打印返回任务的处理时间:1.432703838 pool-1-thread-7 Task7 Taken in seconds:
pool-1-thread-4|任务已处理完成
主线程打印返回任务的处理时间:1.986656899 pool-1-thread-4 Task4 Taken in seconds:
pool-1-thread-10|任务已处理完成
主线程打印返回任务的处理时间:2.530709176 pool-1-thread-10 Task10 Taken in seconds:
pool-1-thread-3|任务已处理完成
主线程打印返回任务的处理时间:2.846413616 pool-1-thread-3 Task3 Taken in seconds:
pool-1-thread-9|任务已处理完成
主线程打印返回任务的处理时间:4.403490409 pool-1-thread-9 Task9 Taken in seconds:
pool-1-thread-5|任务已处理完成
主线程打印返回任务的处理时间:4.542559891 pool-1-thread-5 Task5 Taken in seconds:
所有任务已经完成,关闭线程池
BUILD SUCCESSFUL (total time: 4 seconds)

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JAVA线程基本学习, JAVA多线程的特性= 线程池: 本质上是一个对象池, 用来管理线程资源. 在任务执行前, 需要从线程池中拿出线程来执行. 在任务执行完成之后, 需要把线程放回线程池. 线程池好处: 降低资源的消耗, 线程本身是一种资源, 创建和销毁都会消耗CPU内存, 频繁的创建和销毁会浪内存. 提高任务执行的响应速度, 任务执行时, 可以不必等线程创建完成之后在执行,可以直接获取线程执行任务. 提高线程的可管理性, 线程不能无限制的创建, 需要进行统一的分配, 调优和监控. 不适用线程池坏处: 频繁的线程创建和销毁会占用更多的CPU和内积. 频繁的创建和销毁会对gc产生比较大的压力. 线程太多,线程切换带来的开销将不可忽视. 线程太少, 多核CPU得不到充分利用, 浪费资源. 线程池实现原理: 分为三部分: 核心线程池 线程池 队列 拒绝策略 主要流程: 1.判断核心线程池是否已满, 如果不是, 则创建线程执行任务 2.如果核心线程池满了, 判断队列是否满了, 如果队列没满, 将任务放在队列中 3.如果队列满了, 则判断线程池是否已满, 如果没满, 创建线程执行任务 4.如果线程池满了, 按照拒绝策略对任务进行处理 JDK中提供了一个线程池工厂: Executors ,很多工厂方法, 可以创建多种线程池 1.单一线程池 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 该线程池只有一个线程, 若多个任务被提交到此线程池, 那么会被缓冲到队列,当线程空闲时,按照FIFO的方式进行处理. 2.固定数量线程池 ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5); 和创建单一线程池类似, 不同的是线程池中有多个线程, 可以并行处理任务, 若多个线程任务被提交到此线程池, 会有以下执行过程: 如果线程的数量未达到指定数量, 则创建新线程执行任务. 如果线程池的数量达到了指定数量, 而且此时有线程是空闲的, 则取出空闲线程来执行任务. 如果没有线程是空闲的, 则将任务缓冲到队列, 当线程空闲的时候, 按照FIFO的方式进行处理. FIFO: 出 3.带缓冲的线程池线程池中, 核心线程池长度为0, 线程池最大长度为Integer.MAX_VALUE.以SynchronousQueue作为等待队列, 从而每次往队列中插入一个元素, 必须等待另一个线程从这个队列删除一个元素. 定时调度 4.定时调度的线程池 ExecutorService newCachedThreadPool = Executors.newScheduledThreadPool(); ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5); ExecutorService newWorkStealingPool = Executors.newWorkStealingPool() 线程池家族 线程池的顶层接口是Executor, 这个接口定义了一个核心方法executor(Runnable command), 这个方法最后被ThreadPoolExecutor类实现, 这个方法用来传入任务, 并且该类是线程池的核心类, 构造方法如下 : public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueu, ThreadFactory threadFactory,RejectedExecutionHandler handler); 参数意义: CorePoolSize: 核心线程池大小, 如果核心线程池有空闲的位置, 新的任务就会被核心线程池新建一个线程执行, 执行完毕不会销毁线程, 线程会进入缓冲队列等待再次被运行 MaximunPoolSize: 线程池能创建最大的线程数量, 如果核心线程池和缓冲队列都已经满了, 新的任务就会进来创建新的线程来执行, 但是数量不能超过maximunPoolSize, 否则采取拒绝接受任务策略 KeepAliveTime: 非核心线程能够空闲的最长时间, 超过时间, 线程终止, 这个参数默认只有在线程数量超过核心线程池大小时, 才会起作用. Unit: 时间单位, 和keepAliveTime配合使用. WorkQueue: 缓冲队列, 用来存放等待被执行的任务 ThreadFactory: 线程工厂, 用来创建线程, 一般有三个选择 ArrayBlockingQueue LinkedBlockingQueue SynchronousQueue Handler :拒绝处理策略, 线程数量大于最大线程数量就会拒绝处理策略, 四种策略为 ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 Executor接口有一个子接口ExecutorService, ExecutorService的实现类AbstracExecutorService, 而ThreadPoolExecutor正是AbstracExecutorService的子类. ThreadPoolExecutor还有两个常用的方法shutdown和submit,两者都用来关闭线程池, 但是submit有一个结果返回. 线程池任务执行 当执行executor(Runnable command)方法后, 传入一个任务, public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command)) reject(command); } } (传入的对象实现了Runnable接口, 这就解释了: 为什么实现多线程的方式中继承Thread类不能使用线程池了) 执行过程: 首判断任务是否为空, 为空抛出空指针异常, 否则执行下一个判断: 当前线程数量是否小于核心线程池线程数量, 是,则执行addIfUbderCorePollSize(command)方法, 在核心线程池中创建新的线程, 并且执行这个任务 总结: 如果当前线程池中的数目小于corePollSize, 则每来一个任务, 就会创建一个新的线程去执行这个任务 如果当前线程池中的线程数目>=corePollSize, 则每来一个任务, 会尝试将其添加到缓冲队列中, 如果添加成功, 则该任务会等待空闲线程将其取出去执行, 如果添加失败(一般是以为任务队列已经满了), 则会尝试创建新的线程去执行这个任务. 如果当前线程池中的线程数目达到maximunPoolSize, 则会采取任务拒绝策略进行处理. 如果线程池中的线程数量大于corePoolSize时, 如果某线程空闲时间超过keepAliveTime, 线程将被终止, 直至线程池中的线程数目小于等于corePoolSize; 如果允许核心池中的线程设置存活时间, 那么核心池中的线程空闲时间超过keepAliveTime, 线程也会被终止.
要优化Java接口的响应速度,可以尝试以下几个方法: 1. 使用缓存:对于一些计算耗时较长但结果不经常变动的接口,可以将结果缓存起来,在下次请求时直接返回缓存的结果,避免重复计算。可以使用内存缓存(如Ehcache、Guava Cache)或者分布式缓存(如Redis、Memcached)来实现。 2. 异步处理:对于一些耗时的操作,可以将其放入消息队列中异步处理,这样可以立即返回响应给客户端,而不是等待操作完成。常见的消息队列有Kafka、RabbitMQ等。 3. 使用连接池:对于与其他服务进行通信的接口,如数据库连接、HTTP请求等,使用连接池可以避免频繁地创建和销毁连接,提高性能和响应速度。常见的连接池有HikariCP、Apache Commons Pool等。 4. 数据库优化:如果接口需要大量读取数据库数据,可以优化数据库查询语句、创建索引、拆分表等方式来提高查询性能。 5. 避免阻塞操作:在接口的实现中,尽量避免阻塞操作,如IO操作、网络请求等,可以使用非阻塞IO、多线程等方式来提高并发性能。 6. 代码优化:对接口的实现代码进行优化,如减少不必要的循环和判断、使用高效的数据结构和算法等,可以提高代码执行效率。 以上是一些常见的方法,具体的优化策略需要根据具体的应用场景和需求来确定。在实际应用中,可以通过性能测试和监控工具来评估接口的性能,并对瓶颈进行针对性优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值