当前位置: 首页 > news >正文

Java多线程与JUC

        首先要弄明白一点,Java的线程不等于操作系统级别的线程,Java创建的线程是Java虚拟机管理的,是对底层操作系统线程的封装。提供了统一的API,屏蔽了不同操作系统的差异性。

操作系统级别的线程是由操作系统内核直接管理和调度的实体。它们是 CPU 调度的基本单位。

现代 JVM 实现中,一个 Java 线程通常对应一个操作系统级别的线程,即所谓的 1:1 线程模型常见实现方式:

  • 在 HotSpot JVM(Oracle JDK / OpenJDK)中,每个 Java Thread 对象最终都会映射到一个操作系统的原生线程(native thread)。
  • 这些原生线程由操作系统进行调度,JVM 负责对其进行管理(比如启动、阻塞、唤醒等)。

C语言创建的线程,才是真正意义上的操作系统上的线程。但是C语言在类Unix使用pThread来创建线程,但是在Windows使用的是Win32 API。所以C语言不具备跨平台性,而Java创建线程,无论是什么操作系统,都是调用同一个API,只是JVM版本不同,所以Java可以跨平台

一个运行的JVM实例,对应一个操作系统级别的进程,一个JVM进程,里面可能有多个线程。

        在Java中,线程名和线程ID是Java虚拟机(JVM)内部的概念,主要用于在Java应用程序内标识和区分不同的线程。通过Thread.currentThread().getName()可以获取当前线程的名字,而通过Thread.currentThread().getId()可以获得一个唯一标识该线程的ID。

然而,这些名称和ID与操作系统层面的线程名和ID并不直接对应或相同。具体来说:

线程ID:每个Java线程都有一个在JVM范围内唯一的ID,但这个ID与操作系统的线程ID(通常可以在操作系统的进程管理工具中看到)是不同的。操作系统为每个线程分配的ID是在OS级别唯一的,且它们属于更低级别的实现细节。
  
线程名:你可以给Java中的线程设置名字,这对于调试和日志记录非常有用。但是,这个名字与操作系统给线程指定的名字无关。实际上,在很多情况下,操作系统并不会为单独的线程分配特定的“名字”,而是依赖于进程ID和线程ID来管理和识别线程。

因此,虽然Java提供了一种方便的方式来命名和识别线程,但这些信息主要是为了在JVM和你的Java代码之间使用,并不直接反映到操作系统的线程命名和标识机制上。如果你需要查看底层的操作系统线程信息,可能需要使用操作系统特定的工具或者API,比如Linux下的ps -T -p <PID>命令,或者Windows的任务管理器等,同时结合JVM提供的关于线程映射的信息来进行分析。

1. 进程与线程

进程:进程是程序基本运行的实体。

线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位

启动一个JVM实例=启动一个进程

JVM同时提供多个服务=多线程 

为什么要有多线程?

因为CPU处理数据的速度太快了,等待内存IO的时间或者等待磁盘的IO时间,远远大于CPU处理数据的时间,而这个等待时间,CPU完全可以去处理别的线程任务。

2. 并发和并行

并发:在同一时刻,有多个指令单个CPU上交替执行。

并行:在同一时刻,有多个指令多个CPU上同时执行。

电脑常见的参数:

  • 2核4线程
  • 4核8线程
  • ...
  • 32核64线程

表示的是一台电脑同时能够运行xxx个线程。

3. 多线程的三种实现方式

3.1 继承Thread类的方式

  1. 自定义一个类继承Thread
  2. 重写run方法
  3. 创建子类的对象,并调用start方法启动线程
// 1. 创建一个MyThread类继承Thread
public class MyThread extends Thread{// 2. 重写run方法@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName());}}
}
public class Main {public static void main(String[] args) {// 3. 创建子类对象MyThread myThread01 = new MyThread();MyThread myThread02 = new MyThread();// 4. 调用子类对象的start方法开启线程myThread01.start();myThread02.start();}
}

结果是myThread01和myThread02交替执行

3.2 实现Runnable接口的方式

  1. 自己定义一个类实现Runnable接口
  2. 重写里面的run方法
  3. 创建自己类的对象
  4. 创建一个Thread类的对象,将自己类传进入
public class MyRun implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}
public class Main {public static void main(String[] args) {// 创建任务对象MyRun myRun = new MyRun();// 创建线程对象, 并将myRun任务传递到t1Thread t1 = new Thread(myRun);// 开启线程t1.start();}
}

3.3 利用Callable接口和FutureTask接口方式

无论是继承Thread类创建多线程,还是实现Runnable接口实现多线程,都没有返回值,如果想获得线程的返回值,就要使用这种方式。

1. 创建一个类MyCallable实现Callable接口并重写call方法

2. 创建MyCallable对象

3. 创建FutureTask对象

4. 创建Thread类对象

// 1. 创建一个MyCallable类, 实现Callable接口
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}
}
public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(myCallable);Thread t1 = new Thread(futureTask);t1.start();Integer result = futureTask.get();System.out.println(result);}
}

4. 线程的常用方法

5. 线程池Executors

线程池 主要核心原理:

  1. 创建一个池子,池子是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下次再次提交任务时,不需要创建新的线程,直接复用已有的线程即可。
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。

线程池代码实现:

Executors工具类:线程池的工具类,该工具类提供了很多静态方法,返回不同类型的线程池对象。属于JUC包

1. 创建线程池

 // 1. 创建一个大小为5的线程池, 默认是懒加载的(任务来了才会创建线程)
ExecutorService pool = Executors.newFixedThreadPool(5);

2. 提交任务

// 2. 提交任务
pool.submit(new MyRun());

3. 所以的任务全部执行完毕,关闭线程池

// 3. 销毁线程池
pool.shutdown();

整理代码:

(1)实现Runnable接口

public class MyRun implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}

(2)创建线程池,提交任务

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Main {public static void main(String[] args) {// 1. 创建一个大小为5的线程池, 默认是懒加载的(任务来了才会创建线程)ExecutorService pool = Executors.newFixedThreadPool(5);// 2. 提交任务pool.submit(new MyRun());// 3. 销毁线程池pool.shutdown();}
}

6. 自定义线程池ThreadPoolExecutor

自动一的ThreadPoolExecutor有七个参数

import java.util.concurrent.*;public class Main {public static void main(String[] args) {// 1. 自定义线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(3, // 核心线程数量,不能小于06, // 最大线程数量,不能小于060, // 空闲线程最大存活时间TimeUnit.SECONDS, // 时间单位new LinkedBlockingQueue<>(3), // 任务队列Executors.defaultThreadFactory(), // 创建线程工厂new ThreadPoolExecutor.AbortPolicy() // 任务拒绝策略);// 2. 提交任务pool.submit(new MyRun());}
}

自定义线程池原理:

  1. 创建一个空的池子
  2. 有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程

不断的提交任务时,会有以下三个临界点:

  1. 当核心线程满时,再提交任务就会排队
  2. 当核心线程满,队伍满时,会创建临时线程
  3. 当核心线程满,队伍满,临时线程满,会触发任务拒绝策略。
http://www.lryc.cn/news/576132.html

相关文章:

  • window显示驱动开发—DirectX 图形内核子系统(三)
  • RocketMQ 消息长轮询
  • 集群聊天服务器----CMake的使用
  • ServletConfig ServletContext
  • git add 报错UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xaf in position 42
  • 【Elasticsearch】Linux环境下安装Elasticsearch
  • spring ai入门实例
  • LangChain4j(20)——调用百度地图MCP服务
  • Python Async 编程快速入门 | 超简明异步协程指南
  • java代码规范
  • 自动化保护 AWS ECS Fargate 服务:使用 Prisma Cloud 实现容器安全
  • 阶段二开始-第一章—8天Python从入门到精通【itheima】-116节(封装)
  • 鸿蒙HarmonyOS 5小游戏实践:记忆翻牌(附:源代码)
  • DHT11 STM32 HAL驱动库 整数
  • .NetCore+Vue快速生产框架开发详细方案
  • Chrome浏览器访问https提示“您的连接不是私密连接”问题解决方案
  • 已对接Shopee、Lazada、亚马逊等知名海外电商平台!商派DigiOS-OMS业务中台助力品牌扩展全球业务
  • 《Opto-Electronic Advances》热点论文速览(2025)
  • linux中python虚拟环境和版本的选择
  • 【Linux手册】进程终止:进程退出和信号的响应机制
  • VB.NET,C#字典对象来保存用户数据,支持大小写
  • Selenium 多窗口截图(窗口切换)二次封装方法详解 —— Python 实践
  • 【Python】实现对LGBT+ rights worldwide (2025)数据集的可视化展示
  • MySQL在C中常用的API接口
  • TiDB AUTO_RANDOM 超大主键前端精度丢失排查:JavaScript Number 限制与解决方案
  • 玩转Linux CAN/CAN FD—SocketCAN的使用
  • opensuse安装rabbitmq
  • 【编译原理】期末复习知识总结
  • 【大数据】大数据产品基础篇
  • 【开源项目】「安卓原生3D开源渲染引擎」:Sceneform‑EQR