Java线程池复用线程的秘密你知道吗

yizhihongxing

Java线程池复用线程的秘密你知道吗

线程池的工作原理

线程池是专门用来管理线程的,其主要作用是维护一个空闲的线程队列和一个任务队列,将任务提交到线程池后,线程池会从线程队列中取出一个空闲线程,然后将任务分配给该线程执行,任务执行完毕后该线程就会返回线程队列等待执行下一个任务,这样就能大大提升线程的复用率和运行效率。

线程复用的实现

线程池中的线程是可以复用的,这是因为线程池在创建线程时使用了线程池缓存技术,即如果当前线程池中存在空闲线程,则将该空闲线程从线程池中取出并分配新的任务执行;如果线程池中不存在空闲线程,则会根据线程池的配置创建新的线程。

线程复用的具体实现方式有两种:

  1. 通过线程池缓存技术实现

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE, // 线程池核心线程数
MAX_POOL_SIZE, // 线程池最大线程数
KEEP_ALIVE_TIME, // 线程池空闲线程存活时间
TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
new ArrayBlockingQueue<>(QUEUE_SIZE) // 线程池任务队列
);

线程池会在初始化时创建CORE_POOL_SIZE个线程,如果当任务量过大时,线程池会在达到MAX_POOL_SIZE前扩容,增加新的线程继续执行任务。在线程数达到CORE_POOL_SIZE之后,如果有空闲线程,则该任务会优先分配给空闲线程执行,否则该任务会进入到任务队列中等待执行。

  1. 通过线程复用技术实现

线程复用技术是指将线程池中的线程封装成一个可回收的线程对象,任务执行完以后不会被销毁,而是将该线程返回到线程池中继续待命,等待下一轮任务分配。

```java
public class ReusableThread implements Runnable {

   private ThreadPoolExecutor executor;

   public void setThreadPoolExecutor(ThreadPoolExecutor executor) {
       this.executor = executor;
   }

   @Override
   public void run() {
       while (true) {
           try {
               runnable.run();
           } catch (Throwable t) {
               // handle exception
           } finally {
               if (executor != null) {
                   executor.reuse(this); // 将线程对象返回到线程池中,继续待命
               }
           }
       }
   }

}
```

线程执行完任务后,会调用ThreadPoolExecutor的reuse方法,将线程返回到线程池中,线程池会继续使用该线程执行下一个任务。这样就实现了对线程的复用。

示例说明

示例1

假设有一个服务器需要处理客户端的请求,每个请求都需要新建一个线程来处理。为了避免线程过多导致系统负载过高,可以使用线程池来管理线程,降低系统负载。

public class Server {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5, // 线程池核心线程数
        10, // 线程池最大线程数
        10, // 线程池空闲线程存活时间
        TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
        new LinkedBlockingQueue<>() // 线程池任务队列,使用无界队列
    );

    public void handleRequest(Request request) {
        // 将请求提交到线程池执行
        executor.execute(new Handler(request));
    }

    private class Handler implements Runnable {
        private Request request;

        public Handler(Request request) {
            this.request = request;
        }

        @Override
        public void run() {
            // 处理请求
            // ...
        }
    }
}

在上面的代码中,当有新的请求到达时,会将该请求封装成一个Handler对象,并将该Handler对象提交到线程池中执行。线程池在执行Handler对象时,会从空闲线程队列中选取一个线程执行,如果当前没有空闲线程,则线程池会创建新的线程来执行。执行完Handler对象后,线程会返回线程池,等待下一次任务分配。

示例2

假设有一个需要进行大量计算的任务,可以使用线程池来提高计算效率。下面是一个计算圆周率的示例。

public class PiCalculator {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5, // 线程池核心线程数
        10, // 线程池最大线程数
        10, // 线程池空闲线程存活时间
        TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
        new LinkedBlockingQueue<>() // 线程池任务队列,使用无界队列
    );
    private int numThreads;
    private int iterations;
    private AtomicInteger hitCounter = new AtomicInteger(0);

    public PiCalculator(int numThreads, int iterations) {
        this.numThreads = numThreads;
        this.iterations = iterations;
    }

    public double calculate() {
        for (int i = 0; i < numThreads; i++) {
            executor.submit(new Calculator());
        }

        executor.shutdown();

        try {
            executor.awaitTermination(10, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            // handle interrupt exception
        }

        return 4.0 * hitCounter.get() / (iterations * numThreads);
    }

    private class Calculator implements Runnable {
        private Random rand = new Random();

        @Override
        public void run() {
            int hits = 0;
            for (int i = 0; i < iterations; i++) {
                double x = rand.nextDouble();
                double y = rand.nextDouble();
                if (x * x + y * y <= 1) {
                    hits++;
                }
            }
            hitCounter.addAndGet(hits);
        }
    }
}

在上面的代码中,可以看到使用了线程池来并行计算圆周率。将计算任务分解成多个线程,每个线程计算部分结果,最终汇总得到最终结果。在线程执行完计算任务后,会将线程返回到线程池中,继续待命,等待下一轮计算任务的分配。这样能大大提高计算效率和系统吞吐量。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java线程池复用线程的秘密你知道吗 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Spring Security 和Apache Shiro你需要具备哪些条件

    Spring Security 和 Apache Shiro 都是 Java 应用程序中常用的安全框架,可以用来为应用程序提供身份验证、授权、密码管理、会话管理等安全功能。 要学习 Spring Security 和 Apache Shiro,你需要掌握以下基础条件: Java 编程基础:因为两个框架都是基于 Java 的,所以你需要掌握 Java 编程语言…

    Java 2023年5月20日
    00
  • SpringBoot通过整合Dubbo解决@Reference注解问题

    一、SpringBoot整合Dubbo 利用Dubbo作为RPC(远程过程调用)传输框架,可以将服务分成消费方和提供方两个角色,而Dubbo根据角色的不同提供了不同的注解方式来实现。在消费方和提供方都使用Dubbo的情况下,SpringBoot整合Dubbo优势更加明显。 二、Dubbo @Reference 注解问题 Dubbo的注解@Reference可…

    Java 2023年5月19日
    00
  • nginx+tomcat实现负载均衡,使用redis session共享

    实现负载均衡可以利用反向代理服务器来实现,而Nginx就是一个出色的反向代理服务器,同时,通过Tomcat实现负载均衡的同时还需要使用Redis session共享来实现负载均衡下的session一致性。 下面就是实现“nginx+tomcat实现负载均衡,使用redis session共享”的完整攻略: 环境准备 安装Nginx、Tomcat和Redis;…

    Java 2023年5月19日
    00
  • Spring打包jar包时jsp页面无法访问问题解决

    针对Spring打包jar包时jsp页面无法访问的问题解决,可以依照以下步骤进行操作: 问题解析 在Spring项目中,我们在开发过程中经常使用jsp页面进行开发和展示,当我们将Spring项目打包成jar包并进行部署时,就会出现jsp页面无法访问的问题。原因是嵌入式Web服务器默认不支持jsp引擎。 解决步骤 步骤一:添加插件和依赖 在Spring项目的p…

    Java 2023年6月15日
    00
  • Mybatis通过Mapper代理连接数据库的方法

    Mybatis是一款基于Java的ORM框架,它通过Xml或注解的方式来将Java对象与SQL语句映射起来,把对象持久化到数据库中。在Mybatis中,我们可以通过Mapper代理的方式来调用SQL语句操作数据库。下面是Mybatis通过Mapper代理连接数据库的完整攻略: 步骤一:创建数据库和数据表 首先要创建一个MySQL数据库,然后在数据库中创建一个…

    Java 2023年5月20日
    00
  • 详解Java中Hibernate的基本原理

    详解Java中Hibernate的基本原理 简介 Hibernate是一种运行在Java平台上的ORM框架,它全面支持SQL查询、持久化、数据缓存等功能,能够方便地连接数据库并操作数据。本文将详细讲解Hibernate的基本原理。 Hibernate的基本原理 Hibernate的三个核心API Hibernate的三个核心API分别是: Configura…

    Java 2023年5月20日
    00
  • Java实现字符串转换成可执行代码的方法

    要实现字符串转换成可执行代码,可以通过Java中的动态编译来实现。下面是详细的攻略步骤: 步骤一:引入Java Compiler API Java Compiler API是用于在程序运行时编译Java源码的API。在Java SE 6及以后的版本中,Java Compiler API已经成为标准API的一部分,不需要额外引入。如果您使用的是老版本的Java…

    Java 2023年5月19日
    00
  • java多线程模拟实现售票功能

    Java多线程模拟实现售票功能,主要涉及Java的并发编程和线程同步操作。以下是实现该功能的步骤: 步骤一:创建Ticket类及构造方法 public class Ticket { private int num; public Ticket(int num) { this.num = num; } public int getNum() { return …

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部