线程池和使用

tip: 作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。

推荐:体系化学习Java(Java面试专题)

线程池的目的

线程池是一种用于管理和复用线程的机制。在多线程应用程序中,线程的创建和销毁需要消耗大量的系统资源,而线程池可以通过预先创建一定数量的线程,然后将任务分配给这些线程来避免频繁地创建和销毁线程,从而提高应用程序的性能和效率。线程池还可以控制并发线程的数量,避免过多的线程竞争资源导致的性能下降和系统崩溃。线程池是多线程编程中常用的一种技术,被广泛应用于各种类型的应用程序中。

线程池的参数

线程池的参数包括以下几个:

  1. 核心线程数(corePoolSize):线程池中最少保持的线程数。当有新任务提交时,如果当前线程池中的线程数小于核心线程数,则会创建新的线程来处理任务,即使有空闲的线程也会创建新的线程。

  2. 最大线程数(maximumPoolSize):线程池中最多能创建的线程数。当任务数量超过核心线程数时,线程池会创建新的线程来处理任务,但是最多只能创建最大线程数个线程。

  3. 空闲线程存活时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,空闲线程的存活时间。如果线程在这段时间内没有处理任务,则会被销毁,直到线程池中的线程数量不超过核心线程数。

  4. 阻塞队列(workQueue):用于存放等待执行的任务的队列。当线程池中的线程数量已经达到核心线程数时,新的任务会被放入阻塞队列中等待执行。

  5. 线程工厂(threadFactory):用于创建新的线程。

  6. 时间单位(TimeUnit): 针对救急线程,unit 时间单位

  7. 拒绝策略(rejectedExecutionHandler):当线程池中的线程数量已经达到最大线程数,且阻塞队列已经满了时,新的任务将无法被处理。拒绝策略用于处理这种情况,可以选择抛出异常、直接丢弃任务、丢弃队列中最早的任务或者在调用线程中执行任务等方式。

这些参数可以根据具体应用程序的需求进行调整,以达到最佳的性能和效率。

package com.pany.camp.thread.pool;

import java.util.concurrent.*;

public class ThreadPoolExample {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executor = new ThreadPoolExecutor(
                1,
                1,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        // 提交10个任务给线程池
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            executor.execute(new Runnable() {
                public void run() {
                    System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Task " + taskId + " is completed");
                }
            });
        }

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

在这里插入图片描述

几种封装的线程池

以下是几种常见的封装的线程池:

  1. FixedThreadPool:固定大小线程池,线程数量固定,不会自动调整线程数量。适用于负载比较稳定的情况。
package com.pany.camp.thread.pool;

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

public class FixedThreadPoolExample {

    public static void main(String[] args) {
        // 创建一个固定大小的线程池,大小为3
        ExecutorService executor = Executors.newFixedThreadPool(3);
        // 提交6个任务给线程池
        for (int i = 1; i <= 6; i++) {
            final int taskId = i;
            executor.execute(new Runnable() {
                public void run() {
                    System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Task " + taskId + " is completed");
                }
            });
        }
        // 关闭线程池
        executor.shutdown();
    }
}

我们创建了一个固定大小为3的线程池,然后提交了6个任务给线程池。每个任务都会打印出自己的编号和当前运行的线程名,并且在执行完任务后打印出完成信息。由于线程池大小为3,因此只有3个任务会同时运行,其他任务需要等待空闲线程。最后,我们调用了 executor.shutdown() 方法来关闭线程池。

  1. CachedThreadPool:可缓存线程池,线程数量不固定,会根据任务数量自动调整线程数量。适用于负载比较不稳定的情况。
package com.pany.camp.thread.pool;

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

public class CachedThreadPoolExample {

    public static void main(String[] args) {
        // 创建一个可缓存的线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        // 提交10个任务给线程池
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            executor.execute(new Runnable() {
                public void run() {
                    System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Task " + taskId + " is completed");
                }
            });
        }
        // 关闭线程池
        executor.shutdown();
    }
}

我们创建了一个可缓存的线程池,然后提交了10个任务给线程池。每个任务都会打印出自己的编号和当前运行的线程名,并且在执行完任务后打印出完成信息。由于线程池是可缓存的,因此会根据任务数量自动调整线程数量,如果有空闲线程可以使用,则会直接使用空闲线程,否则会创建新的线程。最后,我们调用了 executor.shutdown() 方法来关闭线程池。

  1. SingleThreadExecutor:单线程线程池,只有一个线程在工作,保证任务按照指定顺序执行。
package com.pany.camp.thread.pool;

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

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        // 创建一个单线程的线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();
        // 提交5个任务给线程池
        for (int i = 1; i <= 5; i++) {
            final int taskId = i;
            executor.execute(new Runnable() {
                public void run() {
                    System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Task " + taskId + " is completed");
                }
            });
        }
        // 关闭线程池
        executor.shutdown();
    }
}

我们创建了一个单线程的线程池,然后提交了5个任务给线程池。每个任务都会打印出自己的编号和当前运行的线程名,并且在执行完任务后打印出完成信息。由于线程池只有一个线程,因此所有任务都会按照顺序依次执行。最后,我们调用了 executor.shutdown() 方法来关闭线程池。

  1. ScheduledThreadPool:定时任务线程池,适用于需要定时执行任务的场景。
package com.pany.camp.thread.pool;

import cn.hutool.core.thread.ThreadUtil;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个大小为3的定时线程池
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
        // 延迟1秒后执行任务
        executor.schedule(() -> System.out.println("Task 1 is running in thread " + Thread.currentThread().getName()), 1, TimeUnit.SECONDS);
        // 延迟2秒后执行任务,之后每3秒执行一次
        executor.scheduleAtFixedRate(() -> System.out.println("Task 2 is running in thread " + Thread.currentThread().getName()), 2, 3, TimeUnit.SECONDS);
        // 延迟3秒后执行任务,之后每5秒执行一次
        executor.scheduleWithFixedDelay(() -> System.out.println("Task 3 is running in thread " + Thread.currentThread().getName()), 3, 5, TimeUnit.SECONDS);
        // 关闭线程池
        ThreadUtil.sleep(10000);
        executor.shutdown();
    }
}

我们创建了一个大小为3的定时线程池,然后提交了3个任务给线程池。第一个任务会延迟1秒后执行,第二个任务会延迟2秒后执行,并且之后每3秒执行一次,第三个任务会延迟3秒后执行,并且之后每5秒执行一次。每个任务都会打印出自己的编号和当前运行的线程名。最后,我们调用了 executor.shutdown() 方法来关闭线程池。

  1. WorkStealingPool:工作窃取线程池,线程数量固定,但是线程之间可以窃取任务执行,提高了任务执行的效率。
package com.pany.camp.thread.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class WorkStealingPoolExample {
    public static void main(String[] args) throws InterruptedException {
        // 获取当前系统可用的处理器数量
        int processors = Runtime.getRuntime().availableProcessors();
        // 创建一个工作窃取线程池
        ExecutorService executor = Executors.newWorkStealingPool(processors);
        // 提交10个任务给线程池
        IntStream.range(1, 11).forEach(i -> executor.submit(() -> {
            System.out.println("Task " + i + " is running in thread " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task " + i + " is completed");
        }));
        // 等待所有任务完成
        executor.awaitTermination(5, TimeUnit.SECONDS);
        // 关闭线程池
        executor.shutdown();
    }
}

,我们使用 Runtime.getRuntime().availableProcessors() 获取当前系统可用的处理器数量,然后创建了一个工作窃取线程池。我们提交了10个任务给线程池,每个任务都会打印出自己的编号和当前运行的线程名,并且在执行完任务后打印出完成信息。由于工作窃取线程池的特性,线程会自动从其他线程窃取任务来执行,因此我们无法预测每个任务会在哪个线程中执行。最后,我们调用了 executor.awaitTermination(5, TimeUnit.SECONDS) 等待所有任务完成,然后调用了 executor.shutdown() 方法来关闭线程池。

这些线程池都是Java中自带的线程池,可以直接使用。不同的线程池适用于不同的场景,根据实际需要选择合适的线程池可以提高应用程序的性能和效率。

封装的线程池的缺点主要有以下几点:

  1. 无法满足特定需求:封装的线程池通常是为了方便使用而设计的,因此可能无法满足某些特定需求。例如,如果需要使用某种特定的线程调度算法,就需要自己实现线程池。

  2. 难以调试:封装的线程池通常会隐藏一些底层细节,这使得在出现问题时难以进行调试。例如,如果线程池中的某个任务出现了异常,我们可能无法知道是哪个任务出了问题。

  3. 可能存在性能问题:封装的线程池通常会提供一些默认配置,这可能无法满足某些高性能的需求。例如,如果需要使用更高效的队列实现,就需要自己实现线程池。

  4. 无界队列可能会引起内存溢出。

因此,在某些情况下,我们可能需要自己实现线程池来满足特定需求。

ThreadPoolExecutor是Java中用于创建和管理线程池的类。它提供了一种方便的方式来执行多个任务,并且可以控制线程的数量和执行方式。 要创建一个ThreadPoolExecutor线程池,可以使用以下步骤: 1. 导入ThreadPoolExecutor类:在代码中导入java.util.concurrent.ThreadPoolExecutor类。 2. 创建ThreadPoolExecutor对象:使用ThreadPoolExecutor的构造函数创建一个线程池对象。构造函数需要传入一些参数,包括核心线程数、最大线程数、线程空闲时间、任务队列等。 3. 提交任务:使用execute()方法或submit()方法向线程池提交任务。execute()方法用于提交不需要返回结果的任务,而submit()方法用于提交需要返回结果的任务。 4. 关闭线程池:在不再需要线程池时,应该调用shutdown()方法来关闭线程池。这会停止接受新任务,并且等待已经提交的任务执行完成。 以下是一个简单的示例代码,演示了如何创建和使用ThreadPoolExecutor线程池: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个线程池,核心线程数为2,最大线程数为5 ExecutorService executor = Executors.newFixedThreadPool(2); // 提交任务到线程池 executor.execute(new Task("Task 1")); executor.execute(new Task("Task 2")); executor.execute(new Task("Task 3")); // 关闭线程池 executor.shutdown(); } static class Task implements Runnable { private String name; public Task(String name) { this.name = name; } @Override public void run() { System.out.println("Executing task: " + name); // 任务逻辑代码 } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

激流丶

感觉小弟写的不错,给点鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值