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则可以用于控制对共享资源的访问。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity中HDRP设置抗锯齿

一、以前抗锯齿的设置方式 【Edit】——>【Project Settings】——>【Quality】——>【Anti-aliasing】 二、HDRP项目中抗锯齿的设置方式 在Hierarchy中——>找到Camera对象——>在Inspector面板上——>【Camera组件】——>【Rendering】——>【Pos…

动手学深度学习72 优化算法

1. 优化算法 任意两点连线&#xff0c;所有线上的值都在集合里面–凸集 在机器学习&#xff0c;凹凸函数的区别&#xff1f; 凸函数表达能力有限 动量法&#xff1a; 比较平滑的改变方向&#xff0c;两个下降方向不一样【冲突】的时候&#xff0c;抵消掉一些使梯度的更新不那…

Linux:进程的优先级 进程切换

文章目录 前言一、进程优先级1.1 基本概念1.2 查看系统进程1.3 PRI和NI1.4 调整优先级1.4.1 top命令1.4.2 nice命令1.4.3 renice命令 二、进程切换2.1 补充概念2.2 进程的运行和切换步骤&#xff08;重要&#xff09; 二、Linux2.6内核进程O(1)调度队列&#xff08;重要&#x…

Python绘制雪花

文章目录 系列目录写在前面技术需求完整代码代码分析1. 代码初始化部分分析2. 雪花绘制核心逻辑分析3. 窗口保持部分分析4. 美学与几何特点总结 写在后面 系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4…

2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序

2023年第十三届MathorCup高校数学建模挑战赛 B题 城市轨道交通列车时刻表优化问题 原题再现&#xff1a; 列车时刻表优化问题是轨道交通领域行车组织方式的经典问题之一。列车时刻表规定了列车在每个车站的到达和出发&#xff08;或通过&#xff09;时刻&#xff0c;其在实际…

AntFlow 0.11.0版发布,增加springboot starter模块,一款设计上借鉴钉钉工作流的免费企业级审批流平台

AntFlow 0.11.0版发布,增加springboot starter模块,一款设计上借鉴钉钉工作流的免费企业级审批流平台 传统老牌工作流引擎比如activiti,flowable或者camunda等虽然功能强大&#xff0c;也被企业广泛采用&#xff0c;然后也存着在诸如学习曲线陡峭&#xff0c;上手难度大&#x…

构建SSH僵尸网络

import argparse import paramiko# 定义一个名为Client的类&#xff0c;用于表示SSH客户端相关操作 class Client:# 类的初始化方法&#xff0c;接收主机地址、用户名和密码作为参数def __init__(self, host, user, password):self.host hostself.user userself.password pa…

小白快速上手 labelme:新手图像标注详解教程

前言 本教程主要面向初次使用 labelme 的新手&#xff0c;详细介绍了如何在 Windows 上通过 Anaconda 创建和配置环境&#xff0c;并使用 labelme 进行图像标注。 1. 准备工作 在开始本教程之前&#xff0c;确保已经安装了 Anaconda。可以参考我之前的教程了解 Anaconda 的下…

AB矩阵秩1乘法,列乘以行

1. AB矩阵相乘 2. 代码测试 python 代码 #!/usr/bin/env python # -*- coding:utf-8 -*- # FileName :ABTest.py # Time :2024/11/17 8:37 # Author :Jason Zhang import numpy as np from abc import ABCMeta, abstractmethodnp.set_printoptions(suppressTrue, pr…

JS学习日记(jQuery库)

前言 今天先更新jQuery库的介绍&#xff0c;它是一个用来帮助快速开发的工具 介绍 jQuery是一个快速&#xff0c;小型且功能丰富的JavaScript库&#xff0c;jQuery设计宗旨是“write less&#xff0c;do more”&#xff0c;即倡导写更少的代码&#xff0c;做更多的事&#xf…

stm32下的ADC转换(江科协 HAL版)

十二. ADC采样 文章目录 十二. ADC采样12.1 ADC的采样原理12.2 STM32的采样基本过程1.引脚与GPIO端口的对应关系2.ADC规则组的四种转换模式(**)2.2 关于转换模式与配置之间的关系 12.3 ADC的时钟12.4 代码实现(ADC单通道 & ADC多通道)1. 单通道采样2. 多通道采样 19.ADC模数…

124. 二叉树中的最大路径和【 力扣(LeetCode) 】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 124. 二叉树中的最大路径和 一、题目描述 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径…

【安全科普】NUMA防火墙诞生记

一、我为啥姓“NUMA” 随着网络流量和数据包处理需求的指数增长&#xff0c;曾经的我面对“高性能、高吞吐、低延迟”的要求&#xff0c;逐渐变得心有余而力不足。 多CPU技术应运而生&#xff0c;SMP&#xff08;对称多处理&#xff09;和NUMA&#xff08;非一致性内存访问&a…

免费送源码:Java+Springboot+MySQL Springboot多租户博客网站的设计 计算机毕业设计原创定制

Springboot多租户博客网站的设计 摘 要 博客网站是当今网络的热点&#xff0c;博客技术的出现使得每个人可以零成本、零维护地创建自己的网络媒体&#xff0c;Blog站点所形成的网状结构促成了不同于以往社区的Blog文化&#xff0c;Blog技术缔造了“博客”文化。本文课题研究的“…

数字IC后端实现之Innovus specifyCellEdgeSpacing和ICC2 set_placement_spacing_rule的应用

昨天帮助社区IC训练营学员远程协助解决一个Calibre DRC案例。通过这个DRC Violation向大家分享下Innovus和ICC2中如何批量约束cell的spacing rule。 数字IC后端手把手实战教程 | Innovus verify_drc VIA1 DRC Violation解析及脚本自动化修复方案 下图所示为T12nm A55项目的Ca…

IntelliJ+SpringBoot项目实战(七)--在SpringBoot中整合Redis

Redis是项目开发中必不可少的缓存工具。所以在SpringBoot项目中必须整合Redis。下面是Redis整合的步骤&#xff1a; &#xff08;1&#xff09;因为目前使用openjweb-sys作为SpringBoot的启动应用&#xff0c;所以在openjweb-sys模块的application-dev.yml中增加配置参数&…

深挖C++赋值

详解赋值 const int a 10; int b a;&a 0x000000b7c6afef34 {56496} &a 0x000000b7c6afef34 {10} 3. &b 0x000000b7c6afef54 {10} 总结&#xff1a; int a 10 是指在内存中&#xff08;栈&#xff09;中创建一个int &#xff08;4 byte&#xff09;大小的空间…

java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程

文章目录 PC Register堆虚拟机栈方法区(Metaspace元空间双亲委派机制类加载器 类装载的执行过程 PC Register 程序计数器&#xff08;Program Counter Register&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;它在 JVM 的内存模型中扮演着非常…

11.12机器学习_特征工程

四 特征工程 1 特征工程概念 特征工程:就是对特征进行相关的处理 一般使用pandas来进行数据清洗和数据处理、使用sklearn来进行特征工程 特征工程是将任意数据(如文本或图像)转换为可用于机器学习的数字特征,比如:字典特征提取(特征离散化)、文本特征提取、图像特征提取。 …

STL序列式容器之list

相较于vector的连续性空间&#xff0c;list相对比较复杂&#xff1b;list内部使用了双向环形链表的方式对数据进行存储&#xff1b;list在增加元素时&#xff0c;采用了精准的方式分配一片空间对数据及附加指针等信息进行存储&#xff1b; list节点定义如下 template<clas…