Java线程&线程池

Thread

Java中的Thread是用于实现多线程编程的类。通过创建Thread对象并调用其start()方法,可以启动一个新的线程并执行指定的代码。

Thread使用

以下是一个简单的示例,展示了如何使用Thread类创建和启动一个线程:

java 复制代码
public class MyThread extends Thread {
    public void run() {
        // 线程要执行的代码
        System.out.println("Hello from a thread!");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
    }
}

在上面的例子中,我们创建了一个继承自Thread类的自定义线程类MyThread。在run()方法中定义了线程要执行的代码,这里只是简单地打印一条消息。在main()方法中,我们创建了一个MyThread对象,并调用其start()方法来启动线程。

启动线程后,它会在后台执行run()方法中的代码。注意,不要直接调用run()方法,而是使用start()方法来启动线程,这样才能实现多线程并发执行的效果。

需要注意的是,Java中还有其他实现多线程的方式,比如实现Runnable接口、使用线程池等。但Thread类是最基本和常用的多线程编程方式之一。

Thread的一些缺点包括:

  1. 多线程编程复杂性:使用多线程编程需要考虑线程同步、死锁、竞态条件等问题,这增加了程序设计和调试的复杂性。

  2. 内存消耗:每个线程都需要一定的内存来维护线程栈、程序计数器等信息,当线程数量过多时,会增加内存消耗。

  3. 上下文切换开销:线程之间的切换需要保存和恢复上下文信息,这会带来一定的开销。

  4. 线程安全问题:多线程环境下,共享资源的访问需要进行同步,否则可能会出现数据竞争和不一致的问题。

  5. 难以调试:多线程程序的调试相对复杂,由于线程的并发执行,问题的复现和定位可能会更加困难。

  6. 可能导致死锁:如果线程之间存在循环等待资源的情况,就可能导致死锁,使得程序无法继续执行。

总之,虽然Java Thread提供了多线程编程的便利性,但也存在一些缺点,需要开发人员在设计和实现时注意避免潜在的问题。

线程池(ExecutorService、ThreadPool)

newCachedThreadPool

newCachedThreadPool是Java中的一个线程池创建方法,它返回一个ThreadPoolExecutor对象,该对象可以用于执行多个任务。它是一个可缓存的线程池,可以根据需要创建新的线程,如果线程池中的线程空闲时间超过指定的时间(默认为60秒),则会被终止并移除。当任务到达时,如果线程池中有空闲线程,则会立即执行任务;如果没有空闲线程,则会创建新的线程来执行任务。适用于执行大量的短期异步任务的场景,不适用于长期运行的任务。

使用示例:

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个可缓存的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 提交任务给线程池执行
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Task " + taskId + " is being executed.");
                }
            });
        }

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

在上面的示例中,我们首先使用Executors.newCachedThreadPool()方法创建一个可缓存的线程池。然后,我们使用execute()方法向线程池提交了10个任务,每个任务都是一个Runnable对象,用于打印任务的编号。最后,我们调用shutdown()方法关闭线程池。

newFixedThreadPool

newFixedThreadPool 是Java中的一个线程池创建方法。它创建一个固定大小的线程池,其中线程的数量是固定的,不会根据任务的数量进行动态调整。

使用 newFixedThreadPool 方法创建的线程池可以同时执行指定数量的任务,当有新的任务提交时,如果线程池中有空闲的线程,则会立即执行;如果线程池中没有空闲的线程,则新的任务会被放入任务队列中等待执行,直到有线程空闲为止。

这种线程池适用于需要控制并发线程数量的场景,可以有效地控制系统资源的使用,避免因为线程过多而导致系统负载过高的问题。

使用示例:

java 复制代码
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++) {
            final int taskNum = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("正在执行任务 " + taskNum);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("任务 " + taskNum + " 执行完毕");
                }
            });
        }

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

上述示例中,我们首先通过Executors.newFixedThreadPool(5)创建了一个固定大小为5的线程池。然后,我们通过executor.execute()方法提交了10个任务给线程池进行执行。每个任务都会打印出自己的任务编号,并在执行完毕后打印出执行完毕的消息。最后,我们通过executor.shutdown()方法关闭线程池。

使用线程池可以更好地管理和控制线程的执行,提高程序的性能和效率。newFixedThreadPool方法创建的线程池具有固定大小,适用于需要控制并发线程数量的场景。

newScheduledThreadPool

newScheduledThreadPool是Java中的一个方法,用于创建一个可调度的线程池。它返回一个ScheduledExecutorService对象,可以用来执行定时任务和周期性任务。

使用示例:

java 复制代码
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        
        // 执行定时任务
        executor.schedule(() -> {
            System.out.println("定时任务执行");
        }, 1, TimeUnit.SECONDS);
        
        // 执行周期性任务
        executor.scheduleAtFixedRate(() -> {
            System.out.println("周期性任务执行");
        }, 0, 2, TimeUnit.SECONDS);
        
        // 关闭线程池
        executor.shutdown();
    }
}

在上述示例中,我们通过Executors.newScheduledThreadPool(5)创建了一个可调度的线程池,其中参数5表示线程池的大小为5。然后,我们可以使用返回的ScheduledExecutorService对象执行定时任务和周期性任务。最后,通过调用executor.shutdown()方法来关闭线程池。

newSingleThreadExecutor

newSingleThreadExecutor 是 Java 中的一个线程池创建方法,用于创建一个只有一个线程的线程池。它的定义如下:

java 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();

这个线程池只会创建一个线程来执行任务,当这个线程执行完一个任务后,会接着执行下一个任务。如果有多个任务提交给这个线程池,它们会按照提交的顺序依次执行。

使用 newSingleThreadExecutor 可以保证任务按照顺序执行,适用于需要顺序执行的场景,比如需要按照任务的提交顺序来处理数据的情况。

示例代码:

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        // 创建一个单线程的线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交任务给线程池执行
        executor.submit(() -> {
            System.out.println("Task 1 is running");
        });

        executor.submit(() -> {
            System.out.println("Task 2 is running");
        });

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

在上面的示例中,我们首先使用Executors.newSingleThreadExecutor()方法创建了一个单线程的线程池。然后,我们使用executor.submit()方法提交了两个任务给线程池执行。最后,我们调用executor.shutdown()方法关闭线程池。

由于newSingleThreadExecutor创建的是一个单线程的线程池,所以任务会按照提交的顺序依次执行。这样可以保证任务之间的顺序性,适用于需要按照顺序执行的场景。

Java提供的四种线程池的优点

Java提供了四种线程池,分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool和SingleThreadPool。它们各自有不同的优点:

  1. FixedThreadPool:固定大小的线程池。它适用于需要控制并发线程数的场景,可以限制线程的数量,避免资源耗尽。适用于执行长期的任务,效率高。

  2. CachedThreadPool:可缓存的线程池。它适用于执行大量的短期任务,可以根据需要自动创建新的线程,线程池的大小可以根据任务的多少自动调整。适用于执行大量的耗时较短的任务,灵活性高。

  3. ScheduledThreadPool:定时任务线程池。它适用于需要定时执行任务的场景,可以按照指定的时间间隔周期性地执行任务。适用于需要定时执行任务的场景,如定时备份、定时统计等。

  4. SingleThreadPool:单线程池。它适用于需要保证任务按照顺序执行的场景,所有任务都在同一个线程中按照指定的顺序执行。适用于需要保证任务按照顺序执行的场景,如消息队列等。

这四种线程池都是通过线程池Executor框架提供的,可以有效地管理和复用线程,提高程序的性能和效率。

自定义线程池

自定义线程池步骤:

  1. 创建一个线程池类,可以命名为CustomThreadPool
  2. 在该类中,需要定义以下属性:
    • ThreadPoolExecutor对象:用于管理线程池的执行和调度。
    • int类型的corePoolSize:指定线程池的核心线程数,即线程池中保持活动状态的线程数。
    • int类型的maximumPoolSize:指定线程池的最大线程数,即线程池中允许的最大线程数。
    • long类型的keepAliveTime:指定线程池中非核心线程的闲置超时时间。
    • BlockingQueue<Runnable>类型的workQueue:用于存放待执行的任务的阻塞队列。
  3. CustomThreadPool类的构造方法中,初始化线程池对象,并设置相关属性。
  4. 提供方法来提交任务到线程池中执行,可以命名为submitTask方法。该方法接受一个Runnable类型的任务作为参数,并将任务提交到线程池中执行。
  5. 可以根据需要,提供其他方法来管理线程池,如获取当前活动线程数、获取线程池中的任务数量等。

示例代码:

java 复制代码
import java.util.concurrent.*;

public class CustomThreadPool {
    private ThreadPoolExecutor threadPool;

    public CustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, BlockingQueue<Runnable> workQueue) {
        threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, workQueue);
    }

    public void submitTask(Runnable task) {
        threadPool.submit(task);
    }

    public int getActiveThreadCount() {
        return threadPool.getActiveCount();
    }

    public int getTaskCount() {
        return threadPool.getQueue().size();
    }
}

使用自定义线程池的示例代码:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个阻塞队列作为任务队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);

        // 创建自定义线程池对象
        CustomThreadPool threadPool = new CustomThreadPool(5, 10, 5000, workQueue);

        // 提交任务到线程池中执行
        for (int i = 0; i < 20; i++) {
            final int taskNum = i;
            threadPool.submitTask(() -> {
                System.out.println("Task " + taskNum + " is running.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + taskNum + " is finished.");
            });
        }

        // 输出线程池中的活动线程数和任务数量
        System.out.println("Active Thread Count: " + threadPool.getActiveThreadCount());
        System.out.println("Task Count: " + threadPool.getTaskCount());

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

以上代码示例中,创建了一个自定义线程池对象threadPool,并提交了20个任务到线程池中执行。然后通过getActiveThreadCount方法和getTaskCount方法获取线程池中的活动线程数和任务数量。最后调用shutdown方法关闭线程池。

相关推荐
浮游本尊13 小时前
Java学习第22天 - 云原生与容器化
java
渣哥15 小时前
原来 Java 里线程安全集合有这么多种
java
间彧15 小时前
Spring Boot集成Spring Security完整指南
java
间彧15 小时前
Spring Secutiy基本原理及工作流程
java
Java水解16 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆18 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学19 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole19 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端
华仔啊19 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端