【Java 基础】22 多线程线程池

1.为什么使用线程池

使用多线程的开发中,频繁创建和销毁线程会带来较大的开销。我们可以使用线程池来有效的管理线程,它是一组预先创建的线程,可以重复使用,提高了线程的利用率,并且能够更好地控制线程的数量和执行方式。

2.线程池有哪些种类

1) 核心接口

线程池的主要接口是 ExecutorExecutorService

  • Executor 接口: 是线程池的根接口,只有一个 execute 方法用于执行任务

    public interface Executor {
        void execute(Runnable var1);
    }
    
  • ExecutorService 接口: 继承自 Executor,扩展了一些功能,如任务提交、任务执行、任务取消等

    public interface ExecutorService extends Executor {
        void shutdown()
        List<Runnable> shutdownNow()
        boolean isShutdown()
        boolean isTerminated()
        boolean awaitTermination()
        <T> Future<T> submit()
        Future<?> submit()
        <T> List<Future<T>> invokeAll()
        <T> T invokeAny()
    }
    

2)线程池分类

FixedThreadPool

固定大小的线程池,每个任务都被分配一个线程

示例代码:

public class Demo {
    public static void main(String[] args) {
        // 创建固定大小(3个)的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        // 提交5个任务
        for (int i = 0; i < 5; i++) {
            executor.execute(new RunnableDemo());
        }
        // 关闭线程池
        executor.shutdown();
    }
}

class RunnableDemo implements Runnable {
    @Override
    public void run() {
        String tName = Thread.currentThread().getName();
        System.out.println(tName + "正在执行...");
    }
}

输出结果:

pool-1-thread-1正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-2正在执行…
pool-1-thread-1正在执行…

在这个例子中,创建了一个固定大小为3的线程池,提交了5个任务。线程池会根据任务数量和线程池大小来调度执行任务。通过 execute() 方法提交任务,线程池会自动选择线程执行任务。最后,调用 shutdown() 方法关闭线程池。

CachedThreadPool

可根据需要创建新线程的线程池,适用于执行很多短期异步任务的小程序

示例代码:

public class Demo {
    public static void main(String[] args) {
        // 创建 CachedThreadPool
        ExecutorService executor = Executors.newCachedThreadPool();
        // 提交任务
        for (int i = 0; i < 5; i++) {
            executor.execute(new RunnableDemo());
        }
        // 关闭线程池
        executor.shutdown();
    }
}

class RunnableDemo implements Runnable {
    @Override
    public void run() {
        String tName = Thread.currentThread().getName();
        System.out.println(tName + "正在执行...");
    }
}

输出结果:

pool-1-thread-1正在执行…
pool-1-thread-3正在执行…
pool-1-thread-2正在执行…
pool-1-thread-5正在执行…
pool-1-thread-4正在执行…

在这个例子中,Executors.newCachedThreadPool() 创建了一个 CachedThreadPool,并提交了5个任务给线程池执行。由于 CachedThreadPool 的特性,线程池会根据需要动态创建新线程,并在一段时间内保留空闲线程以备复用。在这个例子中,任务数量较少,因此线程池可能会重用现有线程,也可能会创建新线程。最后,调用 shutdown() 方法关闭线程池。

SingleThreadExecutor

单线程的线程池,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行

示例代码:

public class Demo {
    public static void main(String[] args) {
        // 创建 SingleThreadExecutor
        ExecutorService executor = Executors.newSingleThreadExecutor();
        // 提交任务
        for (int i = 0; i < 5; i++) {
            executor.execute(new RunnableDemo());
        }
        // 关闭线程池
        executor.shutdown();
    }
}

class RunnableDemo implements Runnable {
    @Override
    public void run() {
        String tName = Thread.currentThread().getName();
        System.out.println(tName + "正在执行...");
    }
}

输出结果:

pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…

在这个例子中,Executors.newSingleThreadExecutor() 创建了一个 SingleThreadExecutor,并提交了5个任务给线程池执行。由于 SingleThreadExecutor 的特性,所有任务会按照提交的顺序在同一个线程中执行。即使有多个任务并发提交,线程池也会保证这些任务一个接一个地执行。最后调用 shutdown() 方法关闭线程池。

ScheduledThreadPool

定时任务线程池,用于定时执行任务

示例代码:

public class Demo {
    public static void main(String[] args) {
        // 创建 ScheduledThreadPool
        ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(3);
        // 延迟任务
        scheduledExecutor.schedule(new RunnableDemo(), 2, TimeUnit.SECONDS);
        // 周期性任务,每隔一秒执行一次
        scheduledExecutor.scheduleAtFixedRate(new RunnableDemo(), 0, 1, TimeUnit.SECONDS);
        // 周期性任务,任务执行完成后延迟一秒再执行下一次
        scheduledExecutor.scheduleWithFixedDelay(new RunnableDemo(), 0, 1, TimeUnit.SECONDS);
        // 主线程等待一段时间,观察任务的执行
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 关闭线程池
        scheduledExecutor.shutdown();
    }
}

class RunnableDemo implements Runnable {
    @Override
    public void run() {
        String tName = Thread.currentThread().getName();
        System.out.println(tName + "正在执行...");
    }
}

输出结果:

pool-1-thread-1正在执行…
pool-1-thread-3正在执行…
pool-1-thread-1正在执行…
pool-1-thread-2正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-2正在执行…
pool-1-thread-1正在执行…
pool-1-thread-3正在执行…
pool-1-thread-3正在执行…
pool-1-thread-1正在执行…
pool-1-thread-2正在执行…
pool-1-thread-3正在执行…
pool-1-thread-1正在执行…
pool-1-thread-2正在执行…
pool-1-thread-3正在执行…
pool-1-thread-1正在执行…
pool-1-thread-2正在执行…

在这个例子中,Executors.newScheduledThreadPool(3) 创建了一个大小为3的 ScheduledThreadPool。通过 schedule 方法可以延迟执行任务,通过 scheduleAtFixedRatescheduleWithFixedDelay 方法可以执行周期性任务。最后通过 shutdown 方法关闭线程池。在主线程等待一段时间后,可以观察到任务的执行情况。

总结

使用线程池是编写高效多线程程序的一种重要方式。它能够降低线程创建和销毁的开销,更好地管理线程的数量,提高系统的性能和响应速度。在选择线程池时,需要根据具体的需求和场景选择合适的线程池类型。

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值