多线程编程之线程池的使用

简介: 前面的几篇文章主要介绍了java中多线程编程中线程的一些概念。比如线程编程中一些常用的API,以及线程的暂停的恢复还有线程的终止。这些都是多线程编程的入门的基础,涉及到的都是单个线程使用,今天这篇文章就来说说在多线程编程中线程池的概念。

1 前言


前面的几篇文章主要介绍了java中多线程编程中线程的一些概念。比如线程编程中一些常用的API,以及线程的暂停的恢复还有线程的终止。这些都是多线程编程的入门的基础,涉及到的都是单个线程使用,今天这篇文章就来说说在多线程编程中线程池的概念。


2 正文


先来说说为什么需要使用线程池


“池”就是水池的意思,我们都知道水池就是用来存储水的,这样就避免我们每次需要用水的时候都需要重新打开水龙头或者去挑水,如果我们做饭的时候每次需要用水都需要打开水龙头或者去挑水,那么做一次饭的开销也太大了(当然开关水龙头的开销是不大的还很方便,这里就是做个比喻,但是一些没通自来水的地方真的是需要挑水,那这个开销还是很大的)。同理,线程池的使用可以:


1、减少创建和销毁线程的次数,每个工作线程都可以被重复使用,可以执行多个任务


2、可以根据系统的性能,调整线程池中工作线程的数量,防止因为消耗过度的内存而导致服务器崩了。


所以通过上面线程池可以带来的好处,我们可以得出线程池的作用


线程池就是限制在系统中可以执行的线程的数量,根据系统的情况,自动或者手动的设置线程的数量,以达到系统运行的最佳效果。使用线程池控制工作线程的数量,多余的工作线程必须排队等候,一个任务执行完毕,再从队列中取组前面的任务开始执行,如果队列中没有等待的线程,那么线程池的资源就处于等待的状态。当一个新的任务需要运行的时候,如果线程池中有等待的工作线程,那么就可以开始运行,否则进入等候队列。


在java里面线程池的最顶部的接口是Executor接口,但是Executor并不是一个线程池,而是一个执行线程的工具而已。


在java中有几个比较重要的类:


1、ExecutorService:这是真正的Java提供的用于管理线程池的类。该类的两个作用:控制线程数量和重用线程


2、ScheduleExecutorService:用于解决那些需要任务重复执行的问题


3、ThreadPoolExecutor:ExecutorSrevice的默认实现


4、ScheduleThreadPoolExecutor


继承ThreadPoolExecutor的ScheduledExecutorService接口的实现,周期性任务调试的类实现。


而在Executors类里提供了一些静态工厂,生成一些常用的线程池。具体常用的线程池实现如下:(返回值都是ExecutorService)


1、Executors.newCachedThreadPool():创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,就会回收部分空闲的线程(60秒没有执行的线程),当任务数量增加时,以可以智能的添加新的线程来处理任务。这个线程池不会对线程池的大小进行限制,线程池的大小完全依赖操作系统(JVM)能够创建的最大线程大小。


package com.jiangxia.chap5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * Executors.newCachedThreadPool
 */
public class Demo7newCachedThreadPool {
    public static void main(String[] args) {
        //创建一个可以缓存的线程池:newCachedThreadPool
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            //线程池运行
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"正在被执行");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
复制代码


eeb25db03efc4457816a77a02e6d01b5~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


通过上面的运行结果可以发现这里的线程池为无限大,当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程。


2、Executors.newFixedThreadPool(int n):创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程池达到最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行而异常结束,那么线程池会补充一个新的线程。


package com.jiangxia.chap5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * newFixedThreadPool:
 */
public class Demo8newFixedThreadPool {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+"正在被执行");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
复制代码


8ca729d1d82648bf8295de0f203285a4~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


这里设置的线程池的大小为5,并且休眠时间为2秒,所以每两秒打印5个线程信息。


3、Executors.newSginleThreadExecutor():创建一个单线程的线程池。这个线程池只有一个工作线程,也就是相当于单线程串行执行所有任务。如果 这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。线程池保证所有任务的执行顺序 会按照 任务的提交顺序执行。


package com.jiangxia.chap5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * newSingleThreadExecutor
 */
public class Demo9NewSingleThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        for (int i = 0; i < 10; i++) {
            final int count = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+"正在被执行,count的值是:"+count);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
复制代码


8ca729d1d82648bf8295de0f203285a4~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


4、Executors.newSingleThreadScheduledExecutor():创建一个单线程用于定时以及周期性执行任务的需求。


package com.jiangxia.chap5;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo10newSingleThreadScheduledExecutor {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        System.out.println("准备执行:"+System.currentTimeMillis());
        // 定时执行
        // scheduleAtFixedRate(需要执行的线程, 第1个需要执行的线程延迟多长时间行, 两个线程间相隔的时间, 使用到时间单位)
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName()+"开始执行于"+System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName()+"执行结束于"+System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },2,5,TimeUnit.SECONDS);
    }
}
复制代码


cb3f682022ef4b018be1fc7eeeab5761~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


5、Executors.newScheduledThreadPool(int n):创建定长的线程池。线程池运行定时以及周期性执行任务的需求。


package com.jiangxia.chap5;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * newScheduledThreadPool
 */
public class Demo11newScheduledThreadPool {
    public static void main(String[] args) {
        //创建一个定长线程池,支持定时及周期性任务执行——延迟执行
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        //延迟1秒执行
                 scheduledThreadPool.schedule(new Runnable() {
                     public void run() {
                        System.out.println("延迟1秒执行");
                     }
                 }, 1, TimeUnit.SECONDS);
        //延迟1秒后每3秒执行一次
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            public void run() {
                System.out.println("延迟1秒后每3秒执行一次");
            }
        }, 1, 3, TimeUnit.SECONDS);
    }
}
复制代码


d8b1bdbb86d14ed7882316b94e7b6f92~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


3 总结


以上就是在java中如何创建线程池的几种方式,合理的使用线程池能够有助于提高系统的性能和效率。


如果觉得文章对你有帮助,就分享给更多的人吧!

目录
相关文章
|
16天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
99 2
|
16天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
46 1
|
1月前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
49 10
|
1月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
1月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
52 3
|
1月前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
43 4
|
14天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
2月前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
72 1
|
3月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
65 1
|
3月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
45 3