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方法关闭线程池。

相关推荐
钱多多_qdd几秒前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha2 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_192849990613 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏15 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境
禁默1 小时前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot