面试题:用过线程池吗?如何自定义线程池?线程池的参数?

简介: 字节跳动面试题:用过线程池吗?如何自定义线程池?线程池的参数?

面试题:用过线程池吗?如何自定义线程池?线程池的参数?


了解线程池


为什么要使用线程池?


使用线程池可以减少线程的创建和销毁次数,提高程序的性能和效率。它可以管理线程的数量、执行任务队列中的任务,并可配置各种参数以适应不同的应用场景。


Java中的线程池


Java提供了java.util.concurrent包来支持线程池的实现。


使用线程池


使用现有线程池


Java提供了Executors类来创建不同类型的线程池,比如FixedThreadPool、CachedThreadPool等。


示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池,大小为5
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 循环提交任务到线程池
        for (int i = 0; i < 10; i++) {
            // 创建一个新的WorkerThread任务,并将其提交到线程池中执行
            Runnable worker = new WorkerThread("Task " + i);
            executor.execute(worker);
        }
        // 关闭线程池,不再接受新的任务
        executor.shutdown();

        // 等待所有线程执行完毕,即线程池中所有任务执行完毕
        while (!executor.isTerminated()) {
        }
        // 所有任务执行完毕后输出提示信息
        System.out.println("所有线程执行完毕");
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 输出当前线程名字、任务开始信息
            System.out.println(Thread.currentThread().getName() + " 开始执行任务:" + task);
            // 模拟任务执行,线程休眠2秒
            processCommand();
            // 输出当前线程名字、任务结束信息
            System.out.println(Thread.currentThread().getName() + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


  • 运行结果解释
pool-1-thread-2 开始执行任务:Task 1
pool-1-thread-5 开始执行任务:Task 4
pool-1-thread-4 开始执行任务:Task 3
pool-1-thread-3 开始执行任务:Task 2
pool-1-thread-1 开始执行任务:Task 0
pool-1-thread-2 任务执行完毕:Task 1
pool-1-thread-1 任务执行完毕:Task 0
pool-1-thread-1 开始执行任务:Task 6
pool-1-thread-5 任务执行完毕:Task 4
pool-1-thread-4 任务执行完毕:Task 3
pool-1-thread-4 开始执行任务:Task 8
pool-1-thread-3 任务执行完毕:Task 2
pool-1-thread-3 开始执行任务:Task 9
pool-1-thread-2 开始执行任务:Task 5
pool-1-thread-5 开始执行任务:Task 7
pool-1-thread-2 任务执行完毕:Task 5
pool-1-thread-3 任务执行完毕:Task 9
pool-1-thread-4 任务执行完毕:Task 8
pool-1-thread-5 任务执行完毕:Task 7
pool-1-thread-1 任务执行完毕:Task 6
所有线程执行完毕


1.任务分配和开始执行阶段:

每个任务都被分配给线程池中的一个线程进行执行。

根据输出,我们可以看到五个任务(Task 0 到 Task 4)被分配给了五个不同的线程(pool-1-thread-1 到 pool-1-thread-5)。


2.任务执行阶段:

每个任务开始执行时,输出了一条消息,指示任务开始执行。

任务的执行过程中,每个线程根据调度依次执行其任务。

在这个阶段,线程池中的线程可能会动态地被重新分配给新的任务,以便更有效地利用系统资源。


3.任务执行完毕阶段:

当任务执行完毕时,输出了一条消息,指示任务执行完毕。

这些消息表明每个任务都顺利地完成了执行。


4.所有线程执行完毕阶段:

最后,输出了一条消息,指示所有线程执行完毕。

这表示所有任务都已经被执行完成,并且线程池中的所有线程都处于空闲状态。


自定义线程池


如果现有的线程池类型不能满足需求,可以自定义线程池。自定义线程池可以根据具体需求配置线程的核心数量、最大数量、任务队列等参数。


示例代码:

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

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, // 核心线程数
                4, // 最大线程数
                10, // 空闲线程存活时间
                TimeUnit.SECONDS, // 存活时间单位
                new LinkedBlockingQueue<Runnable>() // 无界任务队列
        );

        // 提交任务到线程池
        for (int i = 0; i < 6; i++) {
            Runnable worker = new WorkerThread("Task " + i, executor);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();

        // 等待所有线程执行完毕
        while (!executor.isTerminated()) {
            // 等待
        }

        // 输出所有线程执行完毕
        System.out.println("所有线程执行完毕");
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息
        private ThreadPoolExecutor executor; // 线程池

        // 构造函数,用于初始化任务描述信息和线程池
        public WorkerThread(String s, ThreadPoolExecutor executor) {
            this.task = s;
            this.executor = executor;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 获取当前线程池中的核心线程数
            int corePoolSize = executor.getCorePoolSize();

            // 判断当前线程是否为核心线程
            boolean isCoreThread = corePoolSize > 0;

            // 输出是否为核心线程信息
            System.out.println("线程 " + threadName + " 是否为核心线程:" + isCoreThread);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


  • 运行结果
线程 pool-1-thread-2 开始执行任务:Task 1
线程 pool-1-thread-1 开始执行任务:Task 0
线程 pool-1-thread-2 是否为核心线程:true
线程 pool-1-thread-1 是否为核心线程:true
线程 pool-1-thread-1 任务执行完毕:Task 0
线程 pool-1-thread-2 任务执行完毕:Task 1
线程 pool-1-thread-2 开始执行任务:Task 3
线程 pool-1-thread-2 是否为核心线程:true
线程 pool-1-thread-1 开始执行任务:Task 2
线程 pool-1-thread-1 是否为核心线程:true
线程 pool-1-thread-1 任务执行完毕:Task 2
线程 pool-1-thread-1 开始执行任务:Task 4
线程 pool-1-thread-1 是否为核心线程:true
线程 pool-1-thread-2 任务执行完毕:Task 3
线程 pool-1-thread-2 开始执行任务:Task 5
线程 pool-1-thread-2 是否为核心线程:true
线程 pool-1-thread-1 任务执行完毕:Task 4
线程 pool-1-thread-2 任务执行完毕:Task 5
所有线程执行完毕


这个运行结果是一个线程池中的任务执行过程的输出。:


  • 线程 pool-1-thread-2 开始执行任务:Task 1: 线程池中的第二个线程开始执行任务 “Task 1”。
  • 线程 pool-1-thread-1 开始执行任务:Task 0: 线程池中的第一个线程开始执行任务 “Task 0”。
  • 线程 pool-1-thread-2 是否为核心线程:true: 线程池中的第二个线程是一个核心线程。
  • 线程 pool-1-thread-1 是否为核心线程:true: 线程池中的第一个线程是一个核心线程。
  • 线程 pool-1-thread-1 任务执行完毕:Task 0: 线程池中的第一个线程完成了任务 “Task 0”。
  • 线程 pool-1-thread-2 任务执行完毕:Task 1: 线程池中的第二个线程完成了任务 “Task 1”。
  • 线程 pool-1-thread-2 开始执行任务:Task 3: 第二个线程开始执行任务 “Task 3”。
  • 线程 pool-1-thread-2 是否为核心线程:true: 第二个线程仍然是一个核心线程。
  • 线程 pool-1-thread-1 开始执行任务:Task 2: 第一个线程开始执行任务 “Task 2”。
  • 线程 pool-1-thread-1 是否为核心线程:true: 第一个线程仍然是一个核心线程。
  • 线程 pool-1-thread-1 任务执行完毕:Task 2: 第一个线程完成了任务 “Task 2”。
  • 线程 pool-1-thread-1 开始执行任务:Task 4: 第一个线程开始执行任务 “Task 4”。
  • 线程 pool-1-thread-1 是否为核心线程:true: 第一个线程仍然是一个核心线程。
  • 线程 pool-1-thread-2 任务执行完毕:Task 3: 第二个线程完成了任务 “Task 3”。
  • 线程 pool-1-thread-2 开始执行任务:Task 5: 第二个线程开始执行任务 “Task 5”。
  • 线程 pool-1-thread-2 是否为核心线程:true: 第二个线程仍然是一个核心线程。
  • 线程 pool-1-thread-1 任务执行完毕:Task 4: 第一个线程完成了任务 “Task 4”。
  • 线程 pool-1-thread-2 任务执行完毕:Task 5: 第二个线程完成了任务 “Task 5”。
  • 所有线程执行完毕: 所有任务都被线程池中的线程执行完毕了。


自定义线程池参数详解


1.核心线程数(Core Pool Size):


核心线程数是线程池中始终存活的线程数量。

它负责执行任务队列中的任务,即使线程处于空闲状态,也不会被销毁。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CoreThreadPoolExample {
    public static void main(String[] args) {
        // 设置核心线程数为2
        int corePoolSize = 2;

        // 创建一个线程池,指定核心线程数
        ExecutorService executor = Executors.newFixedThreadPool(corePoolSize);

        // 提交任务到线程池
        for (int i = 0; i < 5; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  • 运行结果
线程 pool-1-thread-1 开始执行任务:Task 0
线程 pool-1-thread-2 开始执行任务:Task 1
线程 pool-1-thread-2 任务执行完毕:Task 1
线程 pool-1-thread-1 任务执行完毕:Task 0
线程 pool-1-thread-2 开始执行任务:Task 2
线程 pool-1-thread-1 开始执行任务:Task 3
线程 pool-1-thread-2 任务执行完毕:Task 2
线程 pool-1-thread-1 任务执行完毕:Task 3
线程 pool-1-thread-2 开始执行任务:Task 4
线程 pool-1-thread-2 任务执行完毕:Task 4


  1. 最大线程数(Maximum Pool Size):
  • 最大线程数是线程池中允许的最大线程数量。
  • 当任务队列已满且当前线程数小于最大线程数时,线程池会创建新的线程来执行任务。
import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        int corePoolSize = 5; // 设置核心线程数
        int maxPoolSize = 10; // 设置最大线程数
        long keepAliveTime = 60; // 设置线程空闲时间
        TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 设置任务队列

        // 创建线程池
        ExecutorService executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue);

        // 提交任务到线程池
        for (int i = 0; i < 15; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


  1. 空闲线程存活时间(Keep Alive Time):
  • 当线程池中的线程数量超过核心线程数时,空闲线程等待新任务的最长时间。
  • 超过这个时间后,空闲线程将被销毁,以节省系统资源。
import java.util.concurrent.*;

public class KeepAliveTimeExample {
    public static void main(String[] args) {
        int corePoolSize = 3; // 设置核心线程数
        int maxPoolSize = 5; // 设置最大线程数
        long keepAliveTime = 3; // 设置空闲线程存活时间(秒)
        TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); // 设置任务队列

        // 创建线程池,并设置空闲线程存活时间
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue);

        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


  1. 任务队列(Work Queue):
  • 任务队列用于保存等待执行的任务。
  • 不同类型的任务队列(如ArrayBlockingQueue、LinkedBlockingQueue等)具有不同的特性,如有界性、无界性等。
  • ArrayBlockingQueue:有界队列,容量为10,当任务队列已满时,新的任务无法加入队列,需要等待队列中的任务被处理。
  • LinkedBlockingQueue:无界队列,可以无限制地添加任务,不会出现队列满的情况,但在高负载情况下可能会导致内存溢出。
import java.util.concurrent.*;

public class WorkQueueExample {
    public static void main(String[] args) {
        int corePoolSize = 3; // 设置核心线程数
        int maxPoolSize = 5; // 设置最大线程数
        long keepAliveTime = 3; // 设置空闲线程存活时间(秒)
        TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位

        // 使用有界队列(ArrayBlockingQueue)
        BlockingQueue<Runnable> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
        ThreadPoolExecutor executorWithArrayBlockingQueue = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, arrayBlockingQueue);

        // 使用无界队列(LinkedBlockingQueue)
        BlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>();
        ThreadPoolExecutor executorWithLinkedBlockingQueue = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, linkedBlockingQueue);

        // 提交任务到线程池(使用有界队列)
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executorWithArrayBlockingQueue.execute(worker);
        }

        // 提交任务到线程池(使用无界队列)
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executorWithLinkedBlockingQueue.execute(worker);
        }

        // 关闭线程池(使用有界队列)
        executorWithArrayBlockingQueue.shutdown();

        // 关闭线程池(使用无界队列)
        executorWithLinkedBlockingQueue.shutdown();
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  1. 拒绝策略(Rejected Execution Handler):
  • 当任务队列已满且线程池中的线程数量已达到最大线程数时,线程池无法执行新的任务时的处理策略。
  • 拒绝策略可以是抛出异常、丢弃任务、阻塞等。
  • AbortPolicy:默认的拒绝策略,当任务无法被执行时,会抛出 RejectedExecutionException 异常。

DiscardPolicy:丢弃策略,当任务无法被执行时,会默默地丢弃掉这个任务。

DiscardOldestPolicy:丢弃最旧策略,当任务无法被执行时,会移除最早进入任务队列的任务,然后尝试再次提交当前任务。

CallerRunsPolicy:调用者运行策略,当任务无法被执行时,会由提交任务的线程执行这个任务。

import java.util.concurrent.*;

public class RejectedExecutionHandlerExample {
    public static void main(String[] args) {
        int corePoolSize = 2; // 设置核心线程数
        int maxPoolSize = 3; // 设置最大线程数
        long keepAliveTime = 3; // 设置空闲线程存活时间(秒)
        TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2); // 设置任务队列(有界队列)

        // 创建线程池,并设置空闲线程存活时间和拒绝策略
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue);

        // 设置四种不同的拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 中止策略
        //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 丢弃策略
        //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); // 丢弃最旧策略
        //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 调用者运行策略

        // 提交任务到线程池
        for (int i = 0; i < 5; i++) {
            Runnable worker = new WorkerThread("Task " + i);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 定义一个实现了Runnable接口的WorkerThread类
    static class WorkerThread implements Runnable {
        private String task; // 任务描述信息

        // 构造函数,用于初始化任务描述信息
        public WorkerThread(String s) {
            this.task = s;
        }

        // 实现Runnable接口的run方法,执行具体的任务逻辑
        public void run() {
            // 获取当前线程名字
            String threadName = Thread.currentThread().getName();

            // 输出当前线程名字、任务开始信息
            System.out.println("线程 " + threadName + " 开始执行任务:" + task);

            // 模拟任务执行,线程休眠2秒
            processCommand();

            // 输出当前线程名字、任务结束信息
            System.out.println("线程 " + threadName + " 任务执行完毕:" + task);
        }

        // 模拟任务执行的方法
        private void processCommand() {
            try {
                Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


线程池的应用场景


1. Web服务器


  • 在Web服务器中,经常需要处理大量的客户端请求。
  • 使用线程池可以有效地管理服务器资源,提高并发处理能力,保证服务器的稳定性和性能。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WebServer {
    private static final int MAX_THREADS = 100; // 最大线程数

    public static void main(String[] args) {
        // 创建一个固定大小的线程池,用于处理客户端请求
        ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);

        // 模拟客户端请求
        for (int i = 0; i < 20; i++) {
            // 创建客户端请求任务并提交给线程池执行
            executor.execute(new ClientRequest("Request " + i));
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 定义一个模拟客户端请求的任务类
    static class ClientRequest implements Runnable {
        private String request;

        // 构造函数,初始化客户端请求信息
        public ClientRequest(String request) {
            this.request = request;
        }

        @Override
        public void run() {
            // 模拟处理客户端请求的业务逻辑
            processRequest();
        }

        // 模拟处理客户端请求的业务逻辑
        private void processRequest() {
            // 输出开始处理请求的信息
            System.out.println("开始处理客户端请求:" + request);

            try {
                // 模拟处理请求的耗时操作
                Thread.sleep(1000);

                // 输出请求处理完成的信息
                System.out.println("请求处理完成:" + request);
            } catch (InterruptedException e) {
                // 输出请求处理被中断的信息
                System.out.println("请求处理被中断:" + request);
            }
        }
    }
}

开始处理客户端请求:Request 0
开始处理客户端请求:Request 2
开始处理客户端请求:Request 1
开始处理客户端请求:Request 3
开始处理客户端请求:Request 4
开始处理客户端请求:Request 5
开始处理客户端请求:Request 6
开始处理客户端请求:Request 7
开始处理客户端请求:Request 8
开始处理客户端请求:Request 9
开始处理客户端请求:Request 10
开始处理客户端请求:Request 12
开始处理客户端请求:Request 11
开始处理客户端请求:Request 13
开始处理客户端请求:Request 14
开始处理客户端请求:Request 15
开始处理客户端请求:Request 16
开始处理客户端请求:Request 18
开始处理客户端请求:Request 19
开始处理客户端请求:Request 17
请求处理完成:Request 13
请求处理完成:Request 0
请求处理完成:Request 8
请求处理完成:Request 7
请求处理完成:Request 16
请求处理完成:Request 6
请求处理完成:Request 14
请求处理完成:Request 5
请求处理完成:Request 3
请求处理完成:Request 15
请求处理完成:Request 4
请求处理完成:Request 19
请求处理完成:Request 2
请求处理完成:Request 1
请求处理完成:Request 12
请求处理完成:Request 11
请求处理完成:Request 10
请求处理完成:Request 9
请求处理完成:Request 18
请求处理完成:Request 17

2. 数据库连接池


  • 数据库连接是一种宝贵的资源,频繁地创建和销毁连接会导致系统性能下降。
  • 使用线程池管理数据库连接可以避免资源的频繁分配和释放,提高数据库访问的效率。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DatabaseConnectionPool {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase";
    private static final String DB_USER = "username";
    private static final String DB_PASSWORD = "password";

    private static final int POOL_SIZE = 10; // 连接池大小

    private BlockingQueue<Connection> connectionPool; // 连接池
    private ExecutorService executorService; // 线程池

    public DatabaseConnectionPool() {
        // 初始化连接池
        connectionPool = new ArrayBlockingQueue<>(POOL_SIZE);
        // 初始化线程池
        executorService = Executors.newCachedThreadPool();

        // 初始化连接池中的连接
        for (int i = 0; i < POOL_SIZE; i++) {
            try {
                Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
                connectionPool.put(connection);
            } catch (SQLException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 从连接池中获取连接
    public Connection getConnection() throws InterruptedException {
        return connectionPool.take();
    }

    // 将连接放回连接池
    public void releaseConnection(Connection connection) throws InterruptedException {
        if (connection != null) {
            connectionPool.put(connection);
        }
    }

    // 关闭连接池
    public void closePool() {
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
        }
        connectionPool.forEach(connection -> {
            try {
                if (connection != null && !connection.isClosed()) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
    }
}

在这个数据库连接池的示例中,采用了基于阻塞队列的方式来实现连接池的管理,以及使用线程池来处理数据库连接的请求。


  • 连接池的初始化: 在构造函数中,创建了一个固定大小的阻塞队列作为连接池,并使用 JDBC 连接来填充这个连接池。
  • 获取连接和释放连接: getConnection() 方法用于从连接池中获取连接,releaseConnection() 方法用于将连接放回连接池。
  • 关闭连接池: closePool() 方法用于关闭连接池,在关闭之前会确保连接池中的所有连接都被正确地关闭。


3. 图像处理


  • 在图像处理应用中,通常需要处理大量的图片文件。
  • 使用线程池可以并发地处理多张图片,加快处理速度,提高用户体验。
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ImageProcessor {
    private static final String IMAGE_DIRECTORY = "/path/to/image/directory";
    private static final int THREAD_POOL_SIZE = 5;

    public static void main(String[] args) {
        // 创建固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

        // 获取图像文件列表
        File imageDirectory = new File(IMAGE_DIRECTORY);
        File[] imageFiles = imageDirectory.listFiles();

        // 处理每张图片
        if (imageFiles != null) {
            for (File imageFile : imageFiles) {
                // 提交任务给线程池处理
                executor.submit(new ImageTask(imageFile));
            }
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 图像处理任务类
    static class ImageTask implements Runnable {
        private File imageFile;

        public ImageTask(File imageFile) {
            this.imageFile = imageFile;
        }

        @Override
        public void run() {
            // 执行图像处理逻辑
            processImage(imageFile);
        }

        // 图像处理逻辑
        private void processImage(File imageFile) {
            System.out.println("处理图像文件:" + imageFile.getName());
            // 在这里编写具体的图像处理逻辑
            // 例如,缩放、裁剪、滤镜等操作
        }
    }
}

这个示例演示了如何使用线程池处理图像文件。主要步骤包括:


  1. 创建线程池: 使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,指定了线程池的大小为 5。
  2. 获取图像文件列表: 从指定的图像目录中获取图像文件列表。
  3. 提交任务给线程池: 遍历图像文件列表,为每个图像文件创建一个 ImageTask 任务,并将其提交给线程池执行。
  4. 图像处理逻辑: ImageTask 类实现了 Runnable 接口,其中的 run() 方法定义了图像处理的逻辑。在 processImage() 方法中,可以编写具体的图像处理逻辑,例如图像缩放、裁剪、添加滤镜等操作。
  5. 关闭线程池: 在任务执行完毕后,调用线程池的 shutdown() 方法关闭线程池。


4. 文件上传下载


  • 文件上传下载是Web应用中常见的功能之一。
  • 使用线程池可以并发处理多个上传下载任务,提高文件传输效率,缩短用户等待时间。
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FileTransferManager {
    private static final String UPLOAD_DIRECTORY = "/path/to/upload/directory";
    private static final int THREAD_POOL_SIZE = 5;

    public static void main(String[] args) {
        // 创建固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

        // 获取上传目录中的文件列表
        File uploadDirectory = new File(UPLOAD_DIRECTORY);
        File[] files = uploadDirectory.listFiles();

        // 处理每个文件(模拟上传下载任务)
        if (files != null) {
            for (File file : files) {
                // 提交上传下载任务给线程池处理
                executor.submit(new FileTransferTask(file));
            }
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 文件传输任务类
    static class FileTransferTask implements Runnable {
        private File file;

        public FileTransferTask(File file) {
            this.file = file;
        }

        @Override
        public void run() {
            // 执行文件上传下载逻辑
            transferFile(file);
        }

        // 文件上传下载逻辑
        private void transferFile(File file) {
            System.out.println("处理文件:" + file.getName());
            // 在这里编写具体的文件上传下载逻辑
            // 例如,将文件上传到远程服务器,或者从远程服务器下载文件等操作
        }
    }
}

这个示例展示了如何使用线程池处理文件上传下载任务。主要步骤如下:


  1. 创建线程池: 使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,指定了线程池的大小为 5。
  2. 获取上传目录中的文件列表: 从指定的上传目录中获取文件列表。
  3. 提交任务给线程池: 遍历文件列表,为每个文件创建一个 FileTransferTask 任务,并将其提交给线程池执行。
  4. 文件上传下载逻辑: FileTransferTask 类实现了 Runnable 接口,其中的 run() 方法定义了文件上传下载的逻辑。在 transferFile() 方法中,可以编写具体的文件上传下载逻辑,例如将文件上传到远程服务器,或者从远程服务器下载文件等操作。
  5. 关闭线程池: 在任务执行完毕后,调用线程池的 shutdown() 方法关闭线程池。


相关文章
|
7月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
352 60
【Java并发】【线程池】带你从0-1入门线程池
|
5月前
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
140 18
|
7月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
138 0
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
3月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
233 5
|
7月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
238 20
|
9月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
158 1
|
11月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
138 1
C++ 多线程之初识多线程
|
11月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
192 3