Java由浅入深理解线程池设计和原理1

简介: Java由浅入深理解线程池设计和原理1

1 线程

1.1 什么是线程?什么是进程?

进程:是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存

线程:进程的基本执行单元,一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有线程,进程至少要有一条线程

1.2 java中线程的实现方式有几种?

继承Thread类

public class MyThread extends Thread{
    @Override
    public void run() {
        // 执行自己代码逻辑
        System.out.println("自己线程被执行");
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

实现Runnable接口

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 执行自己代码逻辑
        System.out.println("自己Runnable被执行");
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}

1.3 线程的生命周期是什么?

  • NEW:刚刚创建,没做任何操作
Thread thread = new Thread();
System.out.println(thread.getState());

RUNNABLE:调用run,可以执行,但不代表一定在执行(RUNNING,READY)

thread.start();
System.out.println(thread.getState());
  • BLOCKED:抢不到锁
        final byte[] lock = new byte[0];
        new Thread(new Runnable() {
            public void run() {
                synchronized (lock){
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                synchronized (lock){
                }
            }
        });
        thread2.start();
        Thread.sleep(1000);
        System.out.println(thread2.getState());
  • WAITING
Thread thread2 = new Thread(new Runnable() {
    public void run() {
        LockSupport.park();
    }
});
thread2.start();
Thread.sleep(500);
System.out.println(thread2.getState());
LockSupport.unpark(thread2);
Thread.sleep(500);
System.out.println(thread2.getState());

TIMED_WAITING

Thread thread3 = new Thread(new Runnable() {
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread3.start();
Thread.sleep(500);
System.out.println(thread3.getState());

TERMINATED

//等待1s后再来看
Thread.sleep(1000);
System.out.println(thread.getState());

2 线程存在的问题

2.1 一个线程只能执行一个任务

2.2 线程执行完后销毁,无法复用

public class MyThread extends Thread{
    @Override
    public void run() {
        // 执行自己代码逻辑
        System.out.println("自己线程被执行");
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        // main线程休息5秒,等待myThread执行完成
        TimeUnit.SECONDS.sleep(10);
        // 执行业务代码
        System.out.println("执行其他业务代码");
        // 业务代码执行完成,需要再次执行线程
        myThread.start();
    }
}

2.3 线程过多,导致JVM宕机

3 初识线程池

简介:

在多线程编程中,任务都是一些抽象且离散的工作单元,而线程是使任务异步执行的基本机制。随着应用的扩张,线程和任务管理也变得非常复杂。为了简化这些复杂的线程管理模式,我们需要一个“管理者”来统一管理线程及任务分配,这就是线程池。

在主要大厂的编程规范中,不允许在应用中自行显式地创建线程,线程必须通过线程池提供。由于创建和销毁线程需要时间以及系统资源开销,使用线程池的好处是减少这些开销,解决资源不足的问题。

3.1 了解J.U.C

J.U.C全称:java.util.concurrent,在并发编程中很常用的实用工具类。

在并发编程中很常用的实用工具类,用于完成高并发、处理多线程的一个工具包。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味

3.2 线程池解决了什么问题

  • 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  • 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行
  • 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM
  • 节省CPU切换线程的时间成本(需要保持当前执行线程的现场,并恢复要执行线程的现场)。
  • 提供更强大的功能,延时定时线程池。(Timer vs ScheduledThreadPoolExecutor)

3.3 线程池引发了什么问题

  • 异步任务提交后,如果JVM宕机,已提交的任务会丢失,需要考虑确认机制。
  • 使用不合理,可能导致内存溢出问题
  • 参数过多,代码结构引入数据结构与算法,增加学习难度。

4 线程池的设计思想



5 线程池的原理

5.1 了解线程池类继承结构图

说明:

  • 最常用的是ThreadPoolExecutor
  • 调度用ScheduledThreadPoolExecutor,类似Timer和TimerTask。
  • 任务拆分合并用ForkJoinPool
  • Executors是工具类,协助你创建线程池的

5.2 线程池工作状态

线程池状态

  • RUNNING:初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。RUNNING状态下,能够接收新任务,以及对已添加的任务进行处理。
  • SHUTDOWN:不接收新任务,但能处理已添加的任务。调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
//shutdown后不接受新任务,但是task1,仍然可以执行完成
ExecutorService poolExecutor = Executors.newFixedThreadPool(5);
poolExecutor.execute(new Runnable() {
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println("finish task 1");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
poolExecutor.shutdown();
poolExecutor.execute(new Runnable() {
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
System.out.println("ok");

STOP:不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。调用线程池的shutdownNow()接口时,线程池由(RUNNING 或 SHUTDOWN ) -> STOP

  • 注意:容易引发不可预知的结果!运行中的任务也许还会打印,直到结束,因为调的是Thread.interrupt
//改为shutdownNow后,任务立马终止,sleep被打断,新任务无法提交,task1停止
poolExecutor.shutdownNow();
  • TIDYING:所有的任务已终止,队列中的”任务数量”为0,线程池会变为TIDYING。线程池变为TIDYING状态时,会执行钩子函数terminated(),可以通过重载terminated()函数来实现自定义行为
//自定义类,重写terminated方法
public class MyExecutorService extends ThreadPoolExecutor {
    public MyExecutorService(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
    @Override
    protected void terminated() {
        super.terminated();
        System.out.println("terminated");
    }
    //调用 shutdownNow, ternimated方法被调用打印
    public static void main(String[] args) throws InterruptedException {
        MyExecutorService service = new MyExecutorService(1,2,10000,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(5));
        service.shutdownNow();
    }
}
  • TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED
目录
相关文章
|
8天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
38 6
|
21天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
17天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
17天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
18天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
23天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
90 6
|
21天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
28 2
|
21天前
|
监控 Java 开发者
Java线程管理:守护线程与本地线程的深入剖析
在Java编程语言中,线程是程序执行的最小单元,它们可以并行执行以提高程序的效率和响应性。Java提供了两种特殊的线程类型:守护线程和本地线程。本文将深入探讨这两种线程的区别,并探讨它们在实际开发中的应用。
27 1
|
23天前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
55 1
|
4月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。