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

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~4 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~5 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
程序媛小果5 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot