Java基础-Java多线程机制

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

目录

一、引言

二、多线程的基本概念

1. 线程与进程

2. 多线程与并发

3. 多线程的优势

三、Java多线程的实现方式

1. 继承Thread类

2. 实现Runnable接口

3. 实现Callable接口与Future

四、线程的生命周期

线程状态转换示例

五、同步与互斥

1. 同步的必要性

2. Java中的同步机制

synchronized关键字

Lock接口

volatile关键字

3. 同步的代价

六、线程间通信的深入探索

1. 等待/通知机制

2. Java并发包中的线程间通信

3. 其他并发工具


一、引言

在Java编程中,多线程机制是并发编程的核心部分,它允许程序同时执行多个任务,从而显著提高程序的执行效率和响应速度。多线程不仅在现代应用程序中广泛应用,如服务器后端处理、图形用户界面(GUI)响应、实时数据处理等场景,也是深入理解Java并发包(java.util.concurrent)和其他高级并发工具的基础。本文将从多线程的基本概念、实现方式、生命周期、同步与互斥、线程间通信、线程池等多个方面,对Java多线程机制进行深度解析,并通过代码示例进行具体说明。

二、多线程的基本概念

1. 线程与进程

  • 进程(Process):是系统进行资源分配和调度的基本单位,拥有独立的内存空间和系统资源。每个进程都包含至少一个线程,即主线程。
  • 线程(Thread):是进程中的一个执行实体,也是CPU调度和分派的基本单位。线程共享所属进程的内存空间和系统资源,但每个线程都有独立的执行栈和程序计数器。

2. 多线程与并发

  • 多线程:指在一个程序中同时执行多个线程,每个线程都有自己的执行路径和生命周期。
  • 并发:指在同一时间段内,多个任务交替执行,虽然每个时刻只有一个任务在CPU上执行,但由于CPU切换线程的速度非常快,用户感觉上多个任务在同时执行。

3. 多线程的优势

  • 提高系统响应性能:将耗时的操作放在后台线程中处理,保持主线程的流畅和响应。
  • 提高计算机资源利用率:利用多核处理器的优势,并行执行多个任务。
  • 实现异步编程:主线程可以在等待后台线程完成任务的同时,继续执行其他任务。

三、Java多线程的实现方式

1. 继承Thread类

通过继承java.lang.Thread类并重写其run方法来实现多线程。这种方式简单直接,但存在Java单继承的限制。

public class MyThread extends Thread {  @Override  public void run() {  System.out.println(Thread.currentThread().getName() + " is running.");  // 执行具体任务  }  public static void main(String[] args) {  MyThread t1 = new MyThread();  MyThread t2 = new MyThread();  t1.start(); // 启动线程  t2.start(); // 启动线程  }  
}

2. 实现Runnable接口

通过实现java.lang.Runnable接口的run方法来创建线程。这种方式更为灵活,因为一个类可以实现多个接口,同时也可以通过Thread类的构造器将Runnable实例传递给线程。

public class MyRunnable implements Runnable {  @Override  public void run() {  System.out.println(Thread.currentThread().getName() + " is running.");  // 执行具体任务  }  public static void main(String[] args) {  Thread t1 = new Thread(new MyRunnable());  Thread t2 = new Thread(new MyRunnable());  t1.start();  t2.start();  }  
}

3. 实现Callable接口与Future

Callable接口类似于Runnable,但它可以返回一个结果,并且可以抛出异常。Callable通常与Future一起使用,Future用于表示异步计算的结果。

import java.util.concurrent.*;  public class MyCallable implements Callable<Integer> {  @Override  public Integer call() throws Exception {  // 模拟耗时操作  Thread.sleep(1000);  return 123;  }  public static void main(String[] args) throws ExecutionException, InterruptedException {  ExecutorService executor = Executors.newFixedThreadPool(2);  Future<Integer> future = executor.submit(new MyCallable());  System.out.println("Waiting for result...");  Integer result = future.get(); // 阻塞等待结果  System.out.println("Result: " + result);  executor.shutdown();  }  
}

四、线程的生命周期

线程的生命周期包括新建状态、就绪状态、运行状态、阻塞状态和死亡状态。

  • 新建状态:线程被创建但尚未启动。
  • 就绪状态:线程已准备好执行,但尚未获得CPU时间片。
  • 运行状态:线程获得CPU时间片,正在执行。
  • 阻塞状态:线程由于某种原因(如等待IO操作完成、等待锁资源等)暂停执行。
  • 死亡状态:线程执行完毕或被强制终止,不再执行任何操作。

线程状态转换示例

线程的状态转换是线程执行过程中的自然流程。以下是一个简化的示例,用于说明线程状态之间的转换:

public class ThreadLifecycleExample {  static class MyThread extends Thread {  @Override  public void run() {  System.out.println(Thread.currentThread().getName() + " is in RUNNABLE state.");  synchronized (this) {  try {  wait(); // 进入WAITING状态  } catch (InterruptedException e) {  Thread.currentThread().interrupt(); // 恢复中断状态  }  }  System.out.println(Thread.currentThread().getName() + " resumes and terminates.");  }  }  public static void main(String[] args) throws InterruptedException {  MyThread t = new MyThread();  t.start(); // t进入RUNNABLE状态  // 假设主线程执行了一些操作后,决定唤醒t  Thread.sleep(1000); // 模拟耗时操作  synchronized (t) {  t.notify(); // 唤醒t,使其从WAITING状态进入RUNNABLE状态  }  // t最终会执行完毕,进入TERMINATED状态  }  
}  // 注意:上述代码中的wait()和notify()调用必须放在同步块中,否则将抛出IllegalMonitorStateException。  
// 此外,由于wait()会释放锁,而notify()不会立即让线程进入RUNNABLE状态(需要CPU调度),  
// 因此实际输出可能因线程调度和JVM实现而有所不同。

 在实际应用中,线程的状态转换远比上述示例复杂,特别是在多线程并发环境下,线程的调度和执行顺序往往难以预测。

五、同步与互斥

1. 同步的必要性

在多线程环境下,多个线程可能会同时访问共享资源(如内存中的变量、文件等),这可能导致数据不一致、脏读、脏写等问题。为了确保数据的一致性和完整性,需要对访问共享资源的操作进行同步控制。

2. Java中的同步机制

Java提供了多种同步机制,包括synchronized关键字、Lock接口及其实现(如ReentrantLock)、volatile关键字等。

synchronized关键字

  • 同步方法:在方法声明中加上synchronized关键字,该方法在同一时刻只能被一个线程执行。
  • 同步代码块:使用synchronized(Object lock) { ... }语法,对特定代码块进行同步,其中lock是锁对象。
public class Counter {  private int count = 0;  // 同步方法  public synchronized void increment() {  count++;  }  // 同步代码块  public void incrementWithBlock(Object lock) {  synchronized (lock) {  count++;  }  }  
}

Lock接口

Lock接口提供了比synchronized关键字更灵活的锁定机制,它允许显式地获取和释放锁,以及尝试非阻塞地获取锁。

import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  public class CounterWithLock {  private int count = 0;  private final Lock lock = new ReentrantLock();  public void increment() {  lock.lock(); // 显式获取锁  try {  count++;  } finally {  lock.unlock(); // 显式释放锁  }  }  
}

volatile关键字

volatile关键字用于确保变量的可见性,即当一个线程修改了被volatile修饰的变量的值时,这个新值对其他线程是立即可见的。但volatile不能保证原子性,也不具备互斥性。

public class VolatileExample {  private volatile boolean flag = false;  public void setFlag(boolean flag) {  this.flag = flag;  }  public boolean getFlag() {  return flag;  }  
}

3. 同步的代价

同步虽然能够解决多线程并发带来的问题,但它也引入了额外的开销,如线程等待锁的时间、上下文切换的成本等。因此,在设计多线程程序时,应合理使用同步机制,避免过度同步导致的性能问题。

六、线程间通信的深入探索

在Java中,线程间通信主要依赖于共享内存和相应的同步机制。通过共享内存,线程可以访问和修改同一份数据,而同步机制则确保了在多线程环境下对这些数据的访问是安全且有序的。

1. 等待/通知机制

Java中的wait()notify()/notifyAll()方法是实现线程间通信的经典方式。这些方法是Object类的一部分,因此任何对象都可以作为锁来使用这些机制。

  • wait():使当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法。调用wait()方法时,当前线程必须持有该对象的锁。调用后,当前线程会释放锁并进入等待状态,直到被唤醒。
  • notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的。
  • notifyAll():唤醒在此对象监视器上等待的所有线程。

使用wait()notify()/notifyAll()时,必须注意以下几点:

  • 必须在同步方法或同步代码块中调用这些方法,因为它们依赖于对象锁。
  • 调用wait()的线程会释放锁,并在等待期间无法继续执行。
  • 调用notify()notifyAll()的线程不会立即释放锁,直到它退出同步方法或同步代码块。
  • wait()notify()notifyAll()在调用时必须处理InterruptedException异常。

2. Java并发包中的线程间通信

除了wait()notify()/notifyAll()方法外,Java并发包(java.util.concurrent)还提供了更高级的线程间通信机制,如BlockingQueueCountDownLatchCyclicBarrierSemaphore等。

  • BlockingQueue:支持两个附加操作的队列。这两个附加操作是:在元素从队列中取出时等待队列变为非空,以及在元素添加到队列中时等待队列中有可用空间。BlockingQueue接口是Java并发包中用于生产者-消费者问题的一种重要工具,它提供了一系列线程安全的队列操作。

BlockingQueue的实现包括ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue等。这些实现各有特点,比如ArrayBlockingQueue是一个由数组支持的有界阻塞队列,LinkedBlockingQueue是一个由链表结构组成的有界(但默认大小为Integer.MAX_VALUE)或无界阻塞队列,而PriorityBlockingQueue则是一个支持优先级排序的无界阻塞队列。

3. 其他并发工具

  • CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  • CyclicBarrier:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。在涉及固定大小的线程组时,这些线程必须互相等待,直到所有线程都到达该屏障点,然后从屏障点继续执行。
  • Semaphore:一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在acquire()中阻塞的线程。

这些工具各有用途,在解决复杂的并发问题时非常有用。例如,CountDownLatch可以用于等待一组任务的完成,CyclicBarrier可以用于让一组线程在某个点互相等待然后共同继续执行,而Semaphore则可以用于控制对共享资源的访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/14076.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

springboot 之 整合springdoc2.6 (swagger 3)

版本 springboot 3.3.5 jdk 17 springdoc 2.6.0 依赖pom <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.6.0</version> </dependency>注解对比…

Zabbix部署

1.集群规划 进程虚拟机节点1虚拟机节点2虚拟机节点3zabbix-agent√√√zabbix-server√PostgreSQL√zabbix-web√ 2.准备工作 默认在虚拟机节点2安装kafka、在虚拟机节点3安装redis 2.1关闭3台节点防火墙 sudo systemctl stop firewalld.service sudo systemctl disable fi…

如何优化锚文本来提升关键词排名?

锚文本在SEO中是个小细节&#xff0c;但作用可不小。它不仅能影响外链的质量&#xff0c;还直接影响你的目标关键词排名。你要知道&#xff0c;锚文本并不是随便加上就行&#xff0c;它得讲究技巧和策略。 锚文本的关键词选择一定要精准&#xff0c;且与页面内容高度相关。比如…

java项目-jenkins任务的创建和执行

参考内容: jenkins的安装部署以及全局配置 1.编译任务的general 2.源码管理 3.构建里编译打包然后copy复制jar包到运行服务器的路径 4.部署任务&#xff0c;执行部署脚本

怎么能够制作活码的二维码?在线生成活码的简单技巧

活码是现在很常用的一种二维码类型&#xff0c;可以用来展示日常生活中的视频、音频、图片、文件等多种类型的内容&#xff0c;有效提高内容分享的效率&#xff0c;可以让更多人同时扫码获取内容。使用二维码来展示内容&#xff0c;用户也不需要下载或者保存内容&#xff0c;扫…

谷歌SEO为什么是一场持久战?

很多人在刚开始做SEO时&#xff0c;都会满怀期待&#xff0c;希望能在短时间内看到显著的效果。但很快&#xff0c;他们就会发现&#xff0c;这是一场需要耐心的持久战。谷歌的算法非常复杂&#xff0c;每天都在调整优化&#xff0c;你今天做的改动&#xff0c;可能要几个月后才…

6TS Series TVS 的 解析

6TS Series 600W Transient Voltage Suppresso指的是一系列高性能的瞬态电压抑制二极管&#xff08;Transient Voltage Suppressor&#xff0c;TVS&#xff09;&#xff0c;这些二极管由时源芯微科技&#xff08;TimeSource&#xff09;设计用于保护敏感的电子设备免受雷击、电…

AI绘图最强软件stable diffusion,一文带你迅速了解!

有需要stable diffusion整合包可以扫描下方&#xff0c;免费获取 01 — 什么是 SD ​ Stable Difusion(简称 SD) 其三种概念。 1.用来指代稳定扩散(Stable Diffusion) 技术,如 Midjourney是基于Stable Difusion技术实现的就是指它运用了 Stable Diffusion 的技术原理。 …

Leecode热题100-35.搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…

解决Ultralytics的自定义YOLO模型单GPU可以训练多GPU训练却报错subprocess.CalledProcessError的问题

解决步骤 一、报错详情二、解决思路1. 创建.sh运行文件2. YOLO训练脚本文件3. 终端命令4. 成功运行 一、报错详情 subprocess.CalledProcessError: Command [/home/xxx/anaconda3/envs/openmmlab/bin/python, -m, torch.distributed.run, --nproc_per_node, 3, --master_port,…

Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)

1.Linux的背景介绍 Linux 操作系统的发展历程充满了激情与创新喵&#xff5e;&#x1f380; 萌芽期 (1983 - 1991)&#xff1a;Linux 的历史可追溯到 1983 年&#xff0c;理查德斯托曼 (Richard Stallman) 发起 GNU 计划&#xff0c;目标是创建一个自由软件操作系统。1987 年发…

三款良心实用的桌面待办提醒软件 让工作效率Up提升!

互联网科技的迅速发展&#xff0c;让大家的工作方式也发生了巨大的变化&#xff0c;过去传统的办公方式已然不能适应当下节奏快速发展的时代。在如今工作快节奏的催促下&#xff0c;我们如何才能从琐碎、复杂的工作任务重&#xff0c;找到一条清晰的工作节奏成为效率工作up提升…

热点更新场景,OceanBase如何实现性能优化

案例背景 这个案例来自一个保险行业的客户&#xff1a;他们的核心系统底层采用了OceanBase数据库作为存储解决方案&#xff0c;然而&#xff0c;在系统上线运行后&#xff0c;出现了一个异常情况&#xff0c;执行简单的主键更新语句时SQL执行时间出现了显著的波动。为了迅速定…

【银河麒麟】时间同步工具chrony与ntp对比

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn Chrony介绍 Chrony 是一个用于时间同步的软件。…

fork函数详解

前言 之前我们提到&#xff0c;创建子进程的时候&#xff0c;需要使用fork()函数&#xff0c;其中分别有id 0和id >0的if函数&#xff0c;但是实验表明&#xff0c;两个if函数中的内容都得到了实现。按照我们之前所学&#xff0c;一个变量同一时间只能有一个值&#xff0c;…

二叉排序树

在说二叉排序树之前先考虑这样一个例子&#xff0c;假设我们的数据集开始只有一个数{62}&#xff0c;然后现在需要将88插入数据集&#xff0c;于是数据集成了{62,88}&#xff0c;还保持着从小到大有序&#xff0c;再查找有没有58&#xff0c;没有则插入&#xff0c;可此时要想在…

GitLab 中文版如何禁止从 UI 上下载代码?

本文分享如何通过配置来禁止用户从 GitLab 中文版 UI 界面上下载源代码。 GitLab 中文版也就是极狐GitLab&#xff0c;使用界面和 GitLab 一样。常规下载代码的方式也一样&#xff0c;要么使用 SSH 或者 HTTP 克隆&#xff0c;要么直接从 UI 上下载源代码&#xff1a; 但是有些…

Conmi的正确答案——ESP32导出烧录进芯片的固件

版本&#xff1a;ESP-IDF 4.4.7 系统&#xff1a;Windows 11 相关链接&#xff1a; 官网&#xff1a;Read Flash Contents: read_flash GITHUB独立工具&#xff1a;esptool 命令&#xff1a; # 我这里用的是C3和windows版的EXE工具 esptool.exe --chip ESP32-C3 -p COM17 -b …

vue2+ element ui 集成pdfjs-dist

目录 1. 下载Pdf.js1.1 下载1.2 修改配置1.2.1 将pdfjs-3.8.162-dist复制到项目中1.2.2 解决跨域问题1.2.3 将pdf.worker.js文件复制到public目录下1.2.4 安装 pdfjs-dist1.2.5 前端vue代码(示例) 3. 参考资料 1. 下载Pdf.js 1.1 下载 下载链接&#xff08;官方&#xff09;需…

为什么越来越多的跨境卖家放弃电商平台,转向独立站?

对于做跨境电商的卖家来说&#xff0c;采用多平台、多站点的经营策略非常重要。这样做不仅可以分散风险&#xff0c;避免把所有的钱都押在一个市场上&#xff0c;减少“把所有鸡蛋放在一个篮子里”的风险&#xff0c;还能拓宽销售渠道&#xff0c;帮助卖家赚更多的钱&#xff0…