【多线程】(五)工厂模式和线程池

文章目录

  • 一、工厂模式
  • 二、线程池
    • 2.1 什么是线程池
    • 2.2 Executor 工厂类创建线程池
    • 2.3 ThreadPoolExecutor类创建线程池
  • 三、线程池的实现


一、工厂模式

在Java中,工厂模式是一种创建对象的设计模式,它通过提供一个共同的接口来实例化对象,而不暴露具体实现的细节。工厂模式可以帮助我们解耦对象的创建和使用,提供了一种灵活的方式来创建对象。

在工厂模式中,通常有一个抽象的工厂接口,该接口定义了创建对象的方法。然后,有一个或多个具体的工厂类实现这个接口,每个工厂类负责创建一种具体类型的对象。

下面是一个简单的示例,演示了在Java中如何使用工厂模式:

首先,我们定义一个抽象的产品接口:

public interface Product {
    void doSomething();
}

然后,我们创建两个具体的产品类,实现产品接口:

public class ConcreteProduct1 implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProduct1 do something");
    }
}

public class ConcreteProduct2 implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProduct2 do something");
    }
}

接下来,我们定义一个抽象的工厂接口:

public interface Factory {
    Product createProduct();
}

然后,我们创建两个具体的工厂类,实现工厂接口:

public class ConcreteFactory1 implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}

public class ConcreteFactory2 implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}

最后,我们可以使用工厂来创建具体的产品对象,而不需要直接实例化具体的产品类:

public class Main {
    public static void main(String[] args) {
        Factory factory1 = new ConcreteFactory1();
        Product product1 = factory1.createProduct();
        product1.doSomething();

        Factory factory2 = new ConcreteFactory2();
        Product product2 = factory2.createProduct();
        product2.doSomething();
    }
}

运行上述示例,将会输出以下内容:

ConcreteProduct1 do something
ConcreteProduct2 do something

这样,我们通过工厂模式,实现了创建产品对象的过程和具体产品的实现相分离,客户端只需要通过工厂接口创建产品对象,而不需要关心具体的产品类。这种设计模式使得系统更具有灵活性和可扩展性。

二、线程池

2.1 什么是线程池

线程池是Java中的一个重要概念,它是一种管理和复用线程的机制。线程池维护着一个线程队列,其中包含着多个准备好的线程。当有任务需要执行时,线程池中的线程可以被分配来执行任务,执行完成后又可以返回线程池以供下一次使用

引入线程池的主要目的是为了提高系统的性能和资源利用率。以下是引入线程池的一些好处:

  1. 减少线程创建和销毁的开销
  2. 控制并发线程的数量
  3. 提高响应速度和吞吐量
  4. 提供线程管理和监控
  5. 任务队列和调度策略

2.2 Executor 工厂类创建线程池

Java中的java.util.concurrent.Executors类提供了一个工厂方法来创建不同类型的线程池。这个工厂类提供了一些方便的方法来创建常见类型的线程池。以下是一些常用的线程池创建方法:

  1. FixedThreadPool(固定大小线程池):创建一个固定大小的线程池,一旦线程池达到最大线程数量,其他任务会等待。可以使用 Executors.newFixedThreadPool(int nThreads) 方法创建。
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(new MyTask()); // 提交任务给线程池执行
executor.shutdown(); // 关闭线程池
  1. CachedThreadPool(缓存线程池):根据需要创建新线程,但如果有空闲线程则复用。如果线程空闲时间超过指定的时间(默认为60秒),则会被终止并从线程池中移除。可以使用 Executors.newCachedThreadPool() 方法创建。
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new MyTask()); // 提交任务给线程池执行
executor.shutdown(); // 关闭线程池
  1. SingleThreadExecutor(单线程线程池):只创建一个线程的线程池,保证所有任务按顺序执行。可以使用 Executors.newSingleThreadExecutor() 方法创建。
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new MyTask()); // 提交任务给线程池执行
executor.shutdown(); // 关闭线程池
  1. newScheduledThreadPool(定时线程池):创建一个固定大小的线程池,可用于执行定时任务和周期性任务。
ExecutorService executor = Executors.newScheduledThreadPool(3);
executor.submit(new MyTask()); // 提交任务给线程池执行
executor.shutdown(); // 关闭线程池

这些线程池的使用方法类似,通过 submit() 方法提交任务给线程池执行,shutdown() 方法用于关闭线程池。可以根据实际需求选择适合的线程池类型,并根据需要调整线程池的大小和配置。

2.3 ThreadPoolExecutor类创建线程池

Executors 本质上是对 ThreadPoolExecutor 类的封装,ThreadPoolExecutor类是ExecutorService接口的实现,它提供了更灵活的线程池创建和配置选项。通过使用ThreadPoolExecutor类,可以自定义线程池的行为、线程数量、任务队列、拒绝策略等。

ThreadPoolExecutor 提供了更多的可选参数,可以进一步细化线程池行为的设定,以下是ThreadPoolExecutor类的构造函数及其说明:

构造函数 说明
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) 创建一个线程池,具有指定的核心线程数、最大线程数和任务队列,使用默认的拒绝策略。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) 创建一个线程池,具有指定的核心线程数、最大线程数、任务队列和拒绝策略。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) 创建一个线程池,具有指定的核心线程数、最大线程数、任务队列和线程工厂。线程工厂用于创建线程的实例。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 创建一个线程池,具有指定的核心线程数、最大线程数、任务队列、线程工厂和拒绝策略。线程工厂用于创建线程的实例,拒绝策略用于处理无法执行的任务。

注意:上述构造函数中的参数含义如下:

  • corePoolSize:核心线程数,表示线程池中保持活动状态的线程数量,即使它们处于空闲状态。
  • maximumPoolSize:最大线程数,表示线程池中允许存在的最大线程数量,包括核心线程和非核心线程。
  • keepAliveTime:非核心线程的闲置超时时间,当线程池中的线程数量超过核心线程数时,多余的空闲线程会在指定的时间内被回收。
  • unit:闲置超时时间的单位,例如,TimeUnit.SECONDS表示以秒为单位。
  • workQueue:任务队列,用于存储待执行的任务。
  • threadFactory:线程工厂,用于创建线程的实例。
  • handler:拒绝策略,表示当线程池和任务队列都已满时,新提交的任务如何被拒绝执行。
    • AbortPolicy():超过负荷, 直接抛出异常。
    • CallerRunsPolicy():调用者负责处理。
    • DiscardOldestPolicy():丢弃队列中最老的任务。
    • DiscardPolicy():丢弃新来的任务。

下面是使用ThreadPoolExecutor类创建线程池的示例:

int corePoolSize = 5; // 核心线程数
int maximumPoolSize = 10; // 最大线程数
long keepAliveTime = 60L; // 非核心线程的闲置超时时间
TimeUnit unit = TimeUnit.SECONDS; // 闲置超时时间的单位
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(); // 任务队列
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略

ExecutorService executor = new ThreadPoolExecutor(
    corePoolSize,
    maximumPoolSize,
    keepAliveTime,
    unit,
    workQueue,
    handler
);

三、线程池的实现

创建一个简单的线程池:

// 自定义线程池
class MyThreadPool{
    // 任务队列
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    // n 表示线程池数量
    public MyThreadPool(int n){
        // 创建n个线程
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
               while (true){
                   try {
                       Runnable runnable = queue.take();
                       runnable.run();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
            });
            t.start();
        }
    }

    // 注册任务给线程池
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 以上代码展示了一个简单的自定义线程池实现。该自定义线程池类名为MyThreadPool,通过构造函数传入线程池数量n,并使用一个BlockingQueue作为任务队列来存储待执行的任务。
  • 在构造函数中,创建了n个线程,并在每个线程中使用一个循环来不断从任务队列中取出任务并执行。
  • submit()方法用于向线程池提交任务,将任务放入任务队列中。

你可能感兴趣的:(Java进阶,java,开发语言,线程池)