【Java多线程】对线程池的理解并模拟实现线程池

简介: 【Java多线程】对线程池的理解并模拟实现线程池

1、池

“池”这个概念见到非常多,例如常量池、数据库连接池、线程池、进程池、内存池。


所谓“池”的概念就是:(提高效率)

1、提前把要用的对象准备好

2、用完的对象也不立即释放,留着以备下次使用。

从而大大降低了线程频繁地创建销毁造成的开销。


1.1、线程池

线程池也是如此,提前把要使用的线程,在线程池中准备好,等到需要用时就从池子里取


出,用完之后再归还给池子。


(其中取出和归还操作都是属于纯用户态代码,这就比内核操作更快,更可控)


上面提到了用户态,什么是用户态呢?


       设想一个场景,你在银行柜台办理业务,该业务需要用到身份证复印件,此时有两种选择:一种是柜员拿着你的身份证帮你去复印,一种是你自己去复印。


       显然自己去复印这个操作更加可靠,并且可控一些。如果交给柜员去复印,天知道柜员拿了你的身份证后还去做了什么,因为柜员本身需要处理的事情不止有帮你复印身份证这一件事,可能在帮你复印身份证的路上去做了别的事情,你能做的就只有在柜台前等柜员回来,这种操作显然更加不可控。


你自己去操作就相当于用户态,而柜员帮你操作就相当于是内核态。


而类比到计算机上简单来说就是:


如果一个任务,是在用户程序上完成的,就属于用户态,此时更加的可控。


如果一个任务,是通过系统申请创建线程,需要由内核来完成,就属于内核态,此时更加不可控。


2、ThreadPoolExecutor 线程池类

Java 在标准库中提供了 ThreadPoolExecutor 线程池类



  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲时保持存活的时间(针对临时线程的)
  • unit:存活时间的时间单位(s,min,ms,hour....)
  • workQueue:阻塞队列,和定时器类似,线程池中也可以持有很多个任务
  • threadFactory:线程工厂(这个类中的方法封装了 new Thread 的操作,同时给 Thread 设置了一些属性)
  • handler:拒绝策略,当线程池中的阻塞队列满时,继续往队列中添加任务,此时线程池会执行什么样的操作就称为拒绝策略

针对拒绝策略,又分为四种:


1、继续添加任务直接拒绝接收并报错。【直接报错,新旧任务都不干了】


2、新的任务,由添加任务的线程负责执行。【谁揽的活儿谁干】


3、丢弃最旧的任务,新的添加进来。


4、丢弃新来的任务。



3、Executors 工厂类

ThreadPoolExecutor 本身用起来比较复杂,因此标准库还把 ThreadPoolExecutor 封装了一层,提供了另一个版本。


即 Executors 工厂类,通过这个类来创建出不同的线程池对象。


Executors 创建线程池的几种方式:


  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
public class ExecutorDemo {
    public static void main(String[] args) {
        //将线程数设置为 10
        ExecutorService service = Executors.newFixedThreadPool(10);
        //使用 submit 方法向线程池中提交任务
        service.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello Executors");
            }
        });
    }
}

既然 ThreadPoolExecutor 和 Executors 都可以创建线程池,那么到底该用哪个呢?


正常情况下直接使用 Executors 工厂类即可,当 Executors 工厂类提供的方法无法满足使用时,或者是想要高度定制化线程池时,才去使用 ThreadPoolExecutor。


4、模拟实现线程池

这里模拟写一个固定线程数目的线程池,即模拟使用 newFixedThreadPool 创建的线程池。


分析要点:


1、首先需要提供构造方法,方法中指定创建多少个线程


2、在构造方法中,把这些线程都创建好


3、用一个阻塞队列,将要执行的任务保存起来


4、提供 submit 方法,用于添加新的任务到阻塞队列中


public class MyThreadPoolExecutor {
    // 保存线程的集合
    private List<Thread> threadList = new ArrayList<>();
    // 保存任务的阻塞队列
    private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);
    public MyThreadPoolExecutor(int n) {  //创建n个线程
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                //这些线程需要做的就是不断从任务队列中取出任务并执行
                while (true) {
                    try {
                        // take 带有阻塞功能,为空时自动阻塞
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            //启动线程
            t.start();
            //将线程存入集合中,以备后续使用
            threadList.add(t);
        }
    }
    //submit 添加新的任务到队列中
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}


目录
相关文章
|
2天前
|
存储 SQL 监控
JAVA 线程池的分析和使用
JAVA 线程池的分析和使用
9 0
|
2天前
|
监控 安全 Java
深入理解Java线程池:ThreadPoolExecutor
深入理解Java线程池:ThreadPoolExecutor
9 0
|
2天前
|
Java API
详细探究Java多线程的线程状态变化
Java多线程的线程状态主要有六种:新建(NEW)、可运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)和终止(TERMINATED)。线程创建后处于NEW状态,调用start()后进入RUNNABLE状态,表示准备好运行。当线程获得CPU资源,开始执行run()方法时,它处于运行状态。线程可以因等待锁或调用sleep()等方法进入BLOCKED或等待状态。线程完成任务或发生异常后,会进入TERMINATED状态。
|
2天前
|
存储 安全 Java
Java多线程中线程安全问题
Java多线程中的线程安全问题主要涉及多线程环境下对共享资源的访问可能导致的数据损坏或不一致。线程安全的核心在于确保在多线程调度顺序不确定的情况下,代码的执行结果始终正确。常见原因包括线程调度随机性、共享数据修改以及原子性问题。解决线程安全问题通常需要采用同步机制,如使用synchronized关键字或Lock接口,以确保同一时间只有一个线程能够访问特定资源,从而保持数据的一致性和正确性。
|
2天前
|
监控 安全 Java
Java多线程的使用
Java多线程允许程序同时执行多个任务,提高了系统的整体性能和响应速度。通过创建Thread类或其子类的实例,或使用Runnable接口,Java开发者可以定义并发执行的代码段。多线程在处理复杂任务、资源共享、网络通信等方面具有显著优势,但也需要注意线程安全、同步和死锁等问题。Java提供了丰富的API和工具来处理这些并发问题,使多线程编程更加高效和可靠。
|
2天前
|
API
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
6 1
|
2天前
|
安全 Java 程序员
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二)
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二)
23 4
|
2天前
|
Java 程序员 调度
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(一)
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(一)
19 0
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(一)
|
4天前
|
Java
|
1月前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。