线程池--JAVA

简介: 而线程池就是用来优化线程频繁创建和销毁的场景,减少线程创建、销毁的频率。

虽然线程是轻量级进程,但是如果当创建和销毁的的频率非常之高,那么它也就会消耗很多的资源。

而线程池就是用来优化线程频繁创建和销毁的场景,减少线程创建、销毁的频率。

ExecutorService

JAVA标准库为我们实现了线程池,ExecutorService是一个接口,线程池的创建并不像平常的接口实现那样直接new,而是使用了“工厂模式”。

publicstaticvoidmain(String[] args) {
//创建有4个线程的线程池ExecutorServiceservice=Executors.newFixedThreadPool(4);
//创建一个可以根据任务数量 来自行调整线程数量 的线程池ExecutorServiceservice1=Executors.newCachedThreadPool();
//创建含有一个线程的线程池ExecutorServiceservice2=Executors.newSingleThreadExecutor();
//创建一个含有3个线程的线程池,该线程池可以调度命令在给定时间后延迟运行ScheduledExecutorServiceservice3=Executors.newScheduledThreadPool(3);
}

image.gif

创建好了之后可以利用submit()方法来给里面添加任务

publicstaticvoidmain(String[] args) {
//创建有4个线程的线程池ExecutorServiceservice=Executors.newFixedThreadPool(4);
//添加5个任务for (inti=0; i<5; i++) {
inta=i;
service.submit(()->{
System.out.println(a);
        });
    }
}

image.gif

image.png

但是第四种创建线程池的方法有点特殊如果你想要实现延时执行任务就需要使用schedule()方法。

publicstaticvoidmain(String[] args) {
// 创建一个定时执行任务的线程池,设置核心线程数为3ScheduledExecutorServiceservice=Executors.newScheduledThreadPool(3);
//打印当前时间System.out.println(System.currentTimeMillis());
// 定时执行任务,延迟2秒后开始执行service.schedule(() -> {
// 执行的任务逻辑System.out.println("任务执行时间:"+System.currentTimeMillis());
        }, 2, TimeUnit.SECONDS);
}

image.gif

image.png

上述这几个创建线程池的方法本质上都是将ThreadPoolExecutor进行了封装。

ThreadPoolExecutor

这个类有4中构造方法,可是仔细看就会发现前三种还是调用的的四种,所以本质上是只有一种。

image.png

各个参数的含义

corePoolSize

当前线程池中的核心线程数即当前线程池在空闲时含有的线程数量,也就是当前线程池包含的线程最少数量。

maximumPoolSize

当前线程池中允许存在的最大线程数。

keepAliveTime

当实际线程数大于核心线程数时,多余的空闲线程能够存活的最长时间。

unit

存活时间的单位。

NANOSECONDS:千分之一微秒;

MICROSECONDS:千分之一毫秒;

MILLISECONDS:千分之一秒;

SECONDS:秒;

MINUTES:分钟;

HOURS:小时;

DAYS:天;

workQueue

用于保存待执行任务的队列。

threadFactory

创建新线程时所用的工厂类。

handler

当线程池中的任务满了之后所使用的拒绝策略。

ThreadPoolExecutor.AbortPolicy:直接抛出异常;

ThreadPoolExecutor.CallerRunsPolicy:新添加的任务,由添加任务的线程执行;

ThreadPoolExecutor.DiscardOldestPolicy :丢弃队列中最老的任务,再将新任务添加进任务队列;

ThreadPoolExecutor.DiscardPolicy:丢弃新添加的任务。

线程池的关闭

想要关闭线程池需要使用shutdown()方法

publicstaticvoidmain(String[] args) {
// 创建一个定时执行任务的线程池,设置核心线程数为3ScheduledExecutorServiceservice=Executors.newScheduledThreadPool(3);
//打印当前时间System.out.println(System.currentTimeMillis());
// 定时执行任务,延迟2秒后开始执行service.schedule(() -> {
// 执行的任务逻辑System.out.println("任务执行时间:"+System.currentTimeMillis());
    }, 2, TimeUnit.SECONDS);
}

image.gif

可以看出任务执行完后程序并没有退出。

image.png

publicstaticvoidmain(String[] args) {
// 创建一个定时执行任务的线程池,设置核心线程数为3ScheduledExecutorServiceservice=Executors.newScheduledThreadPool(3);
//打印当前时间System.out.println(System.currentTimeMillis());
// 定时执行任务,延迟2秒后开始执行service.schedule(() -> {
// 执行的任务逻辑System.out.println("任务执行时间:"+System.currentTimeMillis());
    }, 2, TimeUnit.SECONDS);
//主线程休眠一段时间try {
Thread.sleep(2000); // 休眠2秒    } catch (InterruptedExceptione) {
e.printStackTrace();
    }
// 关闭线程池service.shutdown();
}

image.gif

image.png

接下来为了更好的理解线程池,下面是模拟实现一个含有固定线程数的线程池。

模拟实现

先创建一个类名为MyThreadPool里面含有一个属性,类型为BlockingQueue。

publicclassMyThreadPool {
//队列大小为5privateBlockingQueue<Runnable>queue=newArrayBlockingQueue<>(5);
}

image.gif

写一个只有一个参数的有参构造方法,参数为线程池的线程数。

利用循环创建n个线程,每个线程都不断地从队列中拿任务。

publicMyThreadPool(Integern) {
for (inti=0; i<n; i++) {
Threadt=newThread(()->{
while(true) {
try {
Runnablerunnable=queue.take();
runnable.run();
                } catch (InterruptedExceptione) {
thrownewRuntimeException(e);
                }
            }
        });
t.start();
    }
}

image.gif

写一个submit()方法可以给队列中添加任务。

publicvoidsubmit(Runnablerunnable) {
try {
this.queue.put(runnable);
    } catch (InterruptedExceptione) {
thrownewRuntimeException(e);
    }
}

image.gif

此时一个简单的线程池就完成了,下面来进行一下简单的测试:

publicstaticvoidmain(String[] args) {
MyThreadPoolmyThreadPool=newMyThreadPool(5);
for (inti=0; i<40; i++) {
inta=i;
myThreadPool.submit(()->{
System.out.println(a);
        });
    }
}

image.gif

image.png

完整代码

importjava.util.concurrent.ArrayBlockingQueue;
importjava.util.concurrent.BlockingQueue;
//线程池publicclassMyThreadPool {
privateBlockingQueue<Runnable>queue=newArrayBlockingQueue<>(5);
publicMyThreadPool(Integern) {
for (inti=0; i<n; i++) {
Threadt=newThread(()->{
while(true) {
try {
Runnablerunnable=queue.take();
runnable.run();
                    } catch (InterruptedExceptione) {
thrownewRuntimeException(e);
                    }
                }
            });
t.start();
        }
    }
publicvoidsubmit(Runnablerunnable) {
try {
this.queue.put(runnable);
        } catch (InterruptedExceptione) {
thrownewRuntimeException(e);
        }
    }
}

image.gif


目录
相关文章
|
23小时前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第12天】 在现代软件开发中,多线程编程是提升应用程序性能和响应能力的关键手段之一。特别是在Java语言中,由于其内置的跨平台线程支持,开发者可以轻松地创建和管理线程。然而,随之而来的并发问题也不容小觑。本文将探讨Java并发编程的核心概念,包括线程安全策略、锁机制以及性能优化技巧。通过实例分析与性能比较,我们旨在为读者提供一套既确保线程安全又兼顾性能的编程指导。
|
23小时前
|
Java 程序员 调度
Java中的多线程编程:从理论到实践
【5月更文挑战第14天】在现代计算机技术中,多线程编程是一个重要的概念。它允许多个线程并行执行,从而提高程序的运行效率。本文将从理论和实践两个角度深入探讨Java中的多线程编程,包括线程的基本概念、创建和控制线程的方法,以及如何处理线程同步和通信问题。
|
23小时前
|
Java
Java中的多线程编程:基础知识与实践
【5月更文挑战第13天】在计算机科学中,多线程是一种使得程序可以同时执行多个任务的技术。在Java语言中,多线程的实现主要依赖于java.lang.Thread类和java.lang.Runnable接口。本文将深入探讨Java中的多线程编程,包括其基本概念、实现方法以及一些常见的问题和解决方案。
|
23小时前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第13天】 在Java开发中,并发编程是一个复杂且重要的领域。它不仅关系到程序的线程安全性,也直接影响到系统的性能表现。本文将探讨Java并发编程的核心概念,包括线程同步机制、锁优化技术以及如何平衡线程安全和性能。通过分析具体案例,我们将提供实用的编程技巧和最佳实践,帮助开发者在确保线程安全的同时,提升应用性能。
10 1
|
23小时前
|
Java 调度
Java一分钟之线程池:ExecutorService与Future
【5月更文挑战第12天】Java并发编程中,`ExecutorService`和`Future`是关键组件,简化多线程并提供异步执行能力。`ExecutorService`是线程池接口,用于提交任务到线程池,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。通过`submit()`提交任务并返回`Future`对象,可检查任务状态、获取结果或取消任务。注意处理`ExecutionException`和避免无限等待。实战示例展示了如何异步执行任务并获取结果。理解这些概念对提升并发性能至关重要。
17 5
|
23小时前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
15 3
|
23小时前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
57 2
|
23小时前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
44 3
|
23小时前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第11天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个方面,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。我们将通过实例和代码片段来说明这些概念和技术。
4 0
|
23小时前
|
Java 调度
Java并发编程:深入理解线程池
【5月更文挑战第11天】本文将深入探讨Java中的线程池,包括其基本概念、工作原理以及如何使用。我们将通过实例来解释线程池的优点,如提高性能和资源利用率,以及如何避免常见的并发问题。我们还将讨论Java中线程池的实现,包括Executor框架和ThreadPoolExecutor类,并展示如何创建和管理线程池。最后,我们将讨论线程池的一些高级特性,如任务调度、线程优先级和异常处理。