深入理解高并发编程 - 分析创建线程池究竟有哪些方式

1、使用Executors工厂方法:

使用Executors工厂方法创建线程池是一种简单快捷的方式,适用于一些常见的线程池需求。以下是几个示例,演示如何使用Executors工厂方法创建不同类型的线程池:

固定大小线程池 (newFixedThreadPool):

这种类型的线程池会一直保持固定数量的线程在池中,不会自动回收线程。适用于需要限制同时执行的任务数量的场景。

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

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        int numThreads = 5;
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);

        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            executor.submit(new MyRunnable(i));
        }

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

缓存线程池 (newCachedThreadPool):

缓存线程池会根据需要创建新的线程,如果线程池中的线程空闲时间超过指定的时间,则会被回收。适用于任务数量不确定,且需要自动调整线程数的场景。

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

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();

        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            executor.submit(new MyRunnable(i));
        }

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

单线程线程池 (newSingleThreadExecutor):

单线程线程池只有一个工作线程,适用于需要按顺序执行任务的场景。

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

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            executor.submit(new MyRunnable(i));
        }

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

定时任务线程池 (newScheduledThreadPool):

定时任务线程池用于执行定时任务和周期性任务。

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

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        int numThreads = 3;
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(numThreads);

        // 延迟1秒后执行任务
        executor.schedule(new MyRunnable(1), 1, TimeUnit.SECONDS);

        // 延迟2秒后,每3秒执行一次任务
        executor.scheduleAtFixedRate(new MyRunnable(2), 2, 3, TimeUnit.SECONDS);

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

在这些示例中,MyRunnable是一个实现了Runnable接口的自定义任务类。创建线程池后,使用submit方法将任务提交给线程池进行执行,并最终调用shutdown方法关闭线程池。

这些Executors工厂方法提供了一些常见的线程池类型,但在某些情况下,可能需要更精细的线程池配置,这时可以考虑手动创建ThreadPoolExecutor。

2、手动创建ThreadPoolExecutor:

手动创建 ThreadPoolExecutor 允许对线程池的各种参数进行更精细的配置,以满足特定的需求。以下是一个示例,演示如何手动创建 ThreadPoolExecutor:

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

public class ManualThreadPoolExecutorExample {
    public static void main(String[] args) {
        int corePoolSize = 5;
        int maxPoolSize = 10;
        long keepAliveTime = 60; // 60秒
        TimeUnit timeUnit = TimeUnit.SECONDS;
        BlockingQueue workQueue = new LinkedBlockingQueue<>();
        
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue
        );
        
        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            executor.submit(new MyRunnable(i));
        }
        
        executor.shutdown(); // 关闭线程池
    }
}

class MyRunnable implements Runnable {
    private int id;
    
    public MyRunnable(int id) {
        this.id = id;
    }
    
    @Override
    public void run() {
        System.out.println("Task " + id + " is running on thread " + Thread.currentThread().getName());
    }
}

在这个示例中,手动创建了一个 ThreadPoolExecutor,并配置了核心线程数、最大线程数、线程空闲时间等参数。然后,使用 submit 方法将任务提交给线程池进行执行,并最终调用 shutdown 方法关闭线程池。

注意,BlockingQueue 参数用于指定任务队列,用来存储等待执行的任务。在这里,使用了 LinkedBlockingQueue,也可以选择其他的实现,如 ArrayBlockingQueue、PriorityBlockingQueue 等,以适应不同的需求。

手动创建 ThreadPoolExecutor 允许更精细地控制线程池的行为,但也需要更多的配置和管理。在选择线程池类型和参数时,应根据应用的特性和需求进行调整。

3、使用ForkJoinPool:

ForkJoinPool 是 Java 提供的用于支持分治任务的线程池实现。它特别适用于能够将任务拆分成更小的子任务,并且这些子任务可以并行执行的情况。以下是一个使用 ForkJoinPool 创建线程池的示例:

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ForkJoinPoolExample {
    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        
        MyRecursiveAction task = new MyRecursiveAction(0, 10);
        forkJoinPool.invoke(task);
        
        forkJoinPool.shutdown();
    }
}

class MyRecursiveAction extends RecursiveAction {
    private static final int THRESHOLD = 2; // 阈值,小于该值就不再分解任务
    private int start;
    private int end;
    
    public MyRecursiveAction(int start, int end) {
        this.start = start;
        this.end = end;
    }
    
    @Override
    protected void compute() {
        if (end - start <= THRESHOLD) {
            // 执行任务逻辑
            for (int i = start; i <= end; i++) {
                System.out.println("Task is running on thread " + Thread.currentThread().getName() + ": " + i);
            }
        } else {
            // 分解任务
            int mid = (start + end) / 2;
            MyRecursiveAction left = new MyRecursiveAction(start, mid);
            MyRecursiveAction right = new MyRecursiveAction(mid + 1, end);
            
            invokeAll(left, right);
        }
    }
}

在这个示例中,首先创建了一个 ForkJoinPool 实例,然后定义了一个继承自 RecursiveAction 的 MyRecursiveAction 类,用于表示要执行的分治任务。在 compute 方法中,首先检查任务是否足够小,如果是,则执行任务逻辑;否则,将任务分解为两个子任务并使用 invokeAll 方法并行执行。

ForkJoinPool 会根据任务的大小和可用线程数来动态地调度任务的执行,以获得最佳的并行性能。在实际使用中,可以根据任务的特性和复杂度调整阈值,以及分解和执行子任务的逻辑。

注意,ForkJoinPool 适用于能够利用分治并行计算的场景,如递归问题的解决和并行计算任务。

4、使用 ScheduledThreadPoolExecutor 类

ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的子类,专门用于创建带有定时任务功能的线程池。它可以执行定时任务和周期性任务。以下是一个使用 ScheduledThreadPoolExecutor 创建线程池的示例:

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

public class ScheduledThreadPoolExecutorExample {
    public static void main(String[] args) {
        int numThreads = 3;
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(numThreads);
        
        // 延迟1秒后执行任务
        executor.schedule(new MyRunnable(1), 1, TimeUnit.SECONDS);
        
        // 延迟2秒后,每3秒执行一次任务
        executor.scheduleAtFixedRate(new MyRunnable(2), 2, 3, TimeUnit.SECONDS);
        
        // 关闭线程池
        executor.shutdown();
    }
}

class MyRunnable implements Runnable {
    private int id;
    
    public MyRunnable(int id) {
        this.id = id;
    }
    
    @Override
    public void run() {
        System.out.println("Task " + id + " is running on thread " + Thread.currentThread().getName());
    }
}

在这个示例中,使用 Executors.newScheduledThreadPool() 方法创建了一个 ScheduledExecutorService,然后使用 schedule 方法在指定的延迟时间后执行一次任务,使用 scheduleAtFixedRate 方法在指定的延迟时间后开始执行任务,并且每隔一段时间重复执行。

ScheduledThreadPoolExecutor 可以满足定时任务和周期性任务的需求,它能够自动调度任务的执行。当任务执行时间超过任务间隔时间时,ScheduledThreadPoolExecutor 会等待当前任务完成后再启动下一个任务。这种特性对于需要保证任务执行间隔的场景非常有用。

5、使用第三方库(如ThreadPoolExecutor的封装库):

许多第三方库都提供了对 ThreadPoolExecutor 的封装,以便更方便地创建和管理线程池。其中一个常用的库是 Apache Commons Lang 中的 ThreadPoolExecutor,它提供了一些额外的功能和配置选项。以下是一个使用 Apache Commons Lang 的 ThreadPoolExecutor 封装库的示例:

首先,确保已经将 Apache Commons Lang 库添加到项目中。然后,你可以使用 org.apache.commons.lang3.concurrent.BasicThreadFactory 来创建线程池。下面是示例代码:

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

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

public class ThirdPartyThreadPoolExample {
    public static void main(String[] args) {
        int numThreads = 3;
        
        BasicThreadFactory factory = new BasicThreadFactory.Builder()
                .namingPattern("my-pool-%d")
                .daemon(true)
                .build();
        
        ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(
                numThreads, factory);
        
        // 延迟1秒后执行任务
        executor.schedule(new MyRunnable(1), 1, TimeUnit.SECONDS);
        
        // 延迟2秒后,每3秒执行一次任务
        executor.scheduleAtFixedRate(new MyRunnable(2), 2, 3, TimeUnit.SECONDS);
        
        // 关闭线程池
        executor.shutdown();
    }
}

class MyRunnable implements Runnable {
    private int id;
    
    public MyRunnable(int id) {
        this.id = id;
    }
    
    @Override
    public void run() {
        System.out.println("Task " + id + " is running on thread " + Thread.currentThread().getName());
    }
}

在这个示例中,使用 BasicThreadFactory 来配置线程池。通过设置不同的属性,可以定制线程名、守护线程属性等。然后,使用 ScheduledThreadPoolExecutor 创建一个带有定时任务功能的线程池,并使用 schedule 和 scheduleAtFixedRate 方法添加定时任务。

使用第三方库的线程池封装可以帮助更方便地创建和管理线程池,以及提供一些额外的功能选项。

你可能感兴趣的:(#,高并发编程,java)