Java并发编程(一)多线程基础概念

news2024/9/28 3:20:15

概述

  • 多线程技术:基于软件或者硬件实现多个线程并发执行的技术

    • 线程可以理解为轻量级进程,切换开销远远小于进程

    • 在多核CPU的计算机下,使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境

  • 并发编程核心三要素
    • 原子性
      • 原子,即一个不可再被分割的颗粒。在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败
    • 有序性
      • 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
    • 可见性
      • 当多个线程访问内存中同一个变量时,如果其中一个线程对其作了修改,其他线程能立即获取到最新的值
  • 并行 VS 并发
    • 并行:在同一时刻,有多个指令在多个CPU上同时执行

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

  • 同步 VS 异步 && 阻塞 VS 非阻塞

    同步和异步指的对于消息结果的获取是客户端(调用者)主动获取[同步],还是由服务端(提供者)主动通知客户端[异步];阻塞和非阻塞指的一个是客户端(调用者)等待消息处理时的本身状态是挂起[阻塞]还是继续处理其他事[非阻塞];

    • 同步阻塞:客户端主动发起获取结果,而同时一直在等待应答结果
    • 同步非阻塞:客户端主动发起获取结果,而同时不断轮询查看发起的请求是否有应答结果
    • 异步阻塞:客户端发出请求后,服务端会主动通知,而客户端同时一直在等待通知
    • 异步非阻塞:客户端发出请求后,服务端会主动通知,而客户端在获取服务端通知之前已经去处理其他事情
  • 进程 VS 线程
    • 进程:是系统运行的基本单位
      • 独立性: 进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
      • 动态性: 进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
      • 并发性: 任何进程都可以同其他进程一起并发执行
    • 线程:是进程中的单个顺序控制流,是一条执行路径
      • 单线程: 一个进程如果只有一条执行路径,则称为单线程程序
      • 多线程: 一个进程如果有多条执行路径,则称为多线程程序
  • 悲观锁 VS 乐观锁
    • 悲观锁: 每次操作都会加锁,会造成线程阻塞
    • 乐观锁: 每次操作不加锁而是假设没有冲突而去完成某项操作,若因为冲突导致失败就重试,直到成功为止,不会造成阻塞
  • 线程安全

    线程安全问题指的是内存的安全,在每个进程的内存空间中都会有一块共享区域称为堆内存,进程内的所有线程都可以访问到该区域,这就是造成线程安全问题的潜在原因。所以在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存储在内存的数据(主要是全局变量和静态变量)可能被别的线程修改。因此在多线程场景下,我们需要通过相关限制实现线程安全

  • 上下文切换:线程在执行过程中都会有自己的运行条件和状态,这些运行条件和状态我们就称之为线程上下文,这些信息例如程序计数器、虚拟机栈、本地方法栈等信息。当出现以下几种情况时线程就会从占用CPU状态中退出:

    • 线程主动让出CPU,例如调用wait或者sleep等方法
    • 线程的CPU 时间片用完 而退出CPU占用状态 (因为操作系统为了避免某些线程独占CPU导致其他线程饥饿的情况就设定的例如时间分片算法)
    • 线程调用了阻塞类型的系统中断,例如IO请求等
    • 线程被终止或者结束运行
    • 上述的前三种情况都会发生上下文切换为了保证线程被切换在恢复时能够继续执行,所以上下文切换都需要保存线程当前执行的信息,并恢复下一个要执行线程的现场。所以会占用CPU和内存资源,频繁的进行上下文切换就会导致整体效率低下
  • 死锁:指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。死锁的必要条件包括
    • 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用
    • 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放
    • 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
    • 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链

线程分类

  • 普通线程:即前台线程或者用户线程;即应用程序运行的线程
  • 守护线程:即服务线程或者后台线程;优先级较低,作用是为其他线程的运行提供便利服务
    • JRE判断程序是否执行结束的标准是所有的前台执行线程执行完毕,而不管后台线程(守护线程)的状态
    • 守护线程最典型的应用就是GC
    • 除虚拟机提供的如GC守护线程外,开发者也可以设置线程为守护线程

线程生命周期(线程状态)

根据sun官网可知,线程生命周期在java中有以下几种状态:初始(NEW) ,可运行(RUNNABLE)运行(Running),阻塞(BLOCKED)终止(TERMINATED)

  • 初始(NEW): 如果创建Thread类的实例但在调用start()方法之前,线程处于初始(NEW)状态。
  • 可运行(RUNNABLE): 调用start()方法后,线程处于runnable状态,但线程调度程序尚未选择它作为正在运行的线程
  • 运行(RUNNING): 如果线程调度程序已选择它,则线程处于运行状态
  • 阻塞(BLOCKED): 此时线程仍处于活动状态但当前没有资格运行的状态
  • 终止(TERMINATED): 当run()方法退出时,线程处于终止或死亡状态

线程间通信方式

很多业务场景下某个任务都是通过多个线程一起完成,此时就需要线程之间进行通信一起完成该任务,线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺;线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流

  • 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信
    • volatile关键字,该方式是最简单的一种实现方式,volatile修饰的变量是共享内存,保证内存可见性,让多个线程共享一个标志位,当标志位更改的时候就执行不同的线程
  • 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信
    • Object类的 wait/notify方法,执行wait()方法,该线程释放锁资源,然后执行notify() 方法,唤醒其他的线程
    • condition类的 await/signal方法,condition通过lock对象创建,await操作会立刻释放掉锁,进入阻塞状态,signal会唤醒等待队列中的头节点(失败就依次唤醒)
  • 管道流: 管道的输入和输出实际上使用的是一个循环缓冲数组来实现的

创建线程

  • 通过继承Thread,重写其中的run方法,通过调用start方法启动线程时
  • 通过实现Runnable接口,重写其中的run方法,并将自定义类的实例作为一个参数传入Thread获取线程实例,再调用start方法启动线程
  • 通过实现callable接口的,重写其中的call方法。新建Callable的实例,将Callable传入FutureTask得到FutureTask实例,再将FutureTask实例传入Thread获得Thread实例,调用Thread的start方法启动线程,并通过FutureTask的get方法来获取返回值
  • 通过线程池提前为我们准备好线程,我们需要的时候直接取用就可以了,而不需要自己创建

线程Thread常用API

  • start():在使用 new 关键字创建一个线程后(New 状态),只有在 start() 方法执行后,才表示这个线程可运行了(Runnable 状态),至于何时真正运行还要看线程调度器的调度;在线程死亡后,不要再次调用 start() 方法。只能对新建状态的线程调用且只能调用一次 start() 方法,否则将抛出 IllegalThreadStateException 异常
  • run():启动线程是 start() 方法,而不是 run() 方法。如果直接调用 run() 方法,这个线程中的代码会被立即执行,多个线程就无法并发执行了
  • join():等待该线程完成的方法,其他线程将进入等待状态(Waiting 状态),通常由使用线程的程序(线程)调用,如将一个大问题分割为许多小问题,要等待所有的小问题处理后,再进行下一步操作
  • sleep():主动放弃占用的处理器资源,该线程进入阻塞状态(Blocked 状态),指定的睡眠时间超时后,线程进入就绪状态(Runnable),等待线程调度器的调用。
  • yield():主动放弃占用的处理器资源,线程直接进入就绪状态(Runnable),等待线程调度器的调用。可能出现当线程使用yield方法放弃执行后,线程调度器又将该线程调度执行
  • interrupt():没有任何强制线程终止的方法,这个方法只是请求线程终止,这个方法并没有实际的用途,还有 isInterrupted() 方法检查线程是否被中断
  • setDaemon():设置守护进程,该方法必须在 start() 方法之前调用,判断一个线程是不是守护线程,可以使用 isDaemon() 方法判断
  • setPriority():设置线程的优先级,理论上讲线程优先级高更容易被执行。每个线程默认的优先级和父线程(如 main 线程、普通优先级)的优先级相同,线程优先级区间为 1~10,三个静态变量:MIN_PRIORITY = 1、NORM_PRIORITY = 5、MAX_PRIORITY = 10。使用 getPriority() 方法可以查看线程的优先级
  • isAlive():检查线程是否处于活动状态,如果线程处于就绪、运行、阻塞状态,方法返回 true,如果线程处于新建和死亡状态,方法返回 false

线程常见问题

sleep VS wait

  • sleep不会释放锁,只是单纯休眠一会,在给定时间后就会苏醒;而wait则会释放锁,若wait没有设定时间,只能通过notify或者notifyAll唤醒
  • wait常用于线程之间的通信或者交互,而sleep单纯让线程让出执行权。
  • sleep是Thread的方法,因为sleep要做的仅仅是让线程休眠,所以不涉及任何锁释放等逻辑,放在Thread上最合适;而wait是Object的方法,调用wait时会释放锁,并让对象进入WAITING状态,会涉及到资源释放等问题,所以我们需要将wait放在Object 类上

run VS start

  • run: 仅仅是方法,若直接通过对象调用run方法,那么该方法和普通方法没有任何差别,它仅仅是一个名字为run的普通方法
  • start: 开启线程方法,线程就会从用户态转内核态创建线程,并在获取CPU时间片的时候开始运行,然后运行run方法

Thread,Runnable,Callable区别

  • Thread 和 Runnable 区别
    • Thread 是类,而Runnable是接口;如果有其他类需要继承的话肯定是用Runnable接口了
    • Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性
  • Callable和Runnable区别
    • 相同点
      • 都是接口实现,具有良好的扩展性
    • 不同点
      • Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
      • Callable的call方法可以抛出异常,而Runnable的run方法不能抛出异常

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

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

相关文章

无涯教程-Perl - getpriority函数

描述 此函数返回进程(PRIO_PROCESS),进程组(PRIO_PGRP)或用户(PRIO_USER)的当前优先级。 参数WHICH指定要为PRIO_PROCESS,PRIO_PGRP或PRIO_USER之一设置优先级的实体,WHO是要设置的进程ID或用户ID。 WHO的值为0定义了当前流程,流程组或用户。这会在不支持系统getpriority()函…

C++STL简介:提升编程效率与可维护性的利器

目录 引言 一、STL基本概念 二、具体介绍 2.1 容器 2.2 算法 2.2.1 排序(Sort) 2.2.2 查找(Find) 2.3.2 复制(Copy) 2.3 迭代器 三、分析STL 优势: 缺点: 如何学习&#…

《合成孔径雷达成像算法与实现》Figure3.7

代码复现如下: clc clear all close all%参数设置 TBP 100; %时间带宽积 T 10e-6; %脉冲持续时间%参数计算 B TBP/T; …

做BI领域的ChatGPT,思迈特升级一站式ABI平台

8月8日,以「指标驱动 智能决策」为主题,2023 Smartbi V11系列新品发布会在广州丽思卡尔顿酒店开幕。 ​ 后疫情时代,BI发展趋势的观察与应对 在发布会上,思迈特CEO吴华夫在开场致辞中表示,当前大环境背景下&#xf…

行业追踪,2023-08-09

自动复盘 2023-08-09 凡所有相,皆是虚妄。若见诸相非相,即见如来。 k 线图是最好的老师,每天持续发布板块的rps排名,追踪板块,板块来开仓,板块去清仓,丢弃自以为是的想法,板块去留让…

讯飞星火大模型无需代码连接飞书即时消息的方法

1 使用场景 用户联络需求的升级,使智能客服与智能营销迎来飞速增长,客户服务也成为企业经营中非常重要的一环,如何提高客户服务质量和响应速度,一直是企业需要解决的难题。人工智能产品,也正在成为越来越多企业提升业务…

flutter3.0项目集成高得地图

文章目录 1.创建应用:注册高得开发平台 安卓端的设置flutter的集成ios端的设置 先看一下代码运行结果 安卓端真机 ios端真机屏幕录屏 本篇文章demo下载地址 下载后请用xcode修改自己的ios证书真机运行.flutter代码在PG1.dart里面修改 String androidKey “b3392bb7fe532b0eb…

Mac终端利器:Homebrew + iTerm2 + Oh My Zsh 教程

引言 前段时间调整了一下 iTerm2 的环境,感觉比以前好看多了,并且更加高效,这里做一个记录,希望能给大家一些启发。 工具介绍 brew:Mac OS 下强大的包管理工具。iTerm2:iTerm2是 Mac OS 终端的替代品&am…

实现vuex数据持久化处理

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 前言: 方案一 localStorage 介绍 值 示例 JSON.stringify() 介绍 语法 参数 返…

深入JVM - JIT分层编译技术与日志详解

深入JVM - JIT分层编译技术与日志详解 文章目录 深入JVM - JIT分层编译技术与日志详解1. 背景简介2. JIT 编译器2.1. 客户端版本的编译器: C12.2. 服务端版本的编译器: C22.3. Graal JIT 编译器 3. 分层编译技术(Tiered Compilation)3.1. 汇聚两种编译器的优点3.2. 精准优化(Ac…

人工智能、BIM技术、机器学习在智慧工地的应用

人工智能、BIM技术、机器学习在智慧工地的应用 智慧工地云平台是专为建筑施工领域所打造的一体化信息管理平台。通过大数据、云计算、人工智能、BIM、物联网和移动互联网等高科技技术手段,将施工区域各系统数据汇总,建立可视化数字工地。同时&#xff0…

⌈算法进阶⌋图论::并查集——快速理解到熟练运用

目录 一、原理 1. 初始化Init 2. 查询 find 3. 合并 union 二、代码模板 三、练习 1、 990.等式方程的可满足性 (中等) 2、 1061. 按字典序排列最小的等效字符串 (中等) 3、账户合并 (较难&am…

QFontDialog

QFontDialog QFont字体类使用QFont类QFontDialog类的静态API简单的使用 QFontDialog类是QDialog的子类, 通过这个类我们可以得到一个进行字体属性设置的对话框窗口, 和前边介绍的对话框类一样, 我们只需要调用这个类的静态成员函数就可以得到想要的窗口了。 QFont字体类 关于…

Android自定义侧滑Item

源码地址&#xff1a;https://github.com/LanSeLianMa/CustomizeView/tree/master/cehuaitem 使用方式一&#xff1a;XML布局中直接使用 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com…

python怎么判断变量的数据类型

在编程的世界里&#xff0c;了解各种数据类型是非常重要的。在Python中&#xff0c;有着丰富的数据类型用于存储和处理不同类型的数据。掌握这些数据类型的定义和作用&#xff0c;我们能够更好地在程序中管理和操作数据&#xff0c;提高代码的效率和可读性。 Python中常见的数据…

使用node搭建服务器,前端自己写接口,将vue或react打包后生成的dist目录在本地运行

使用node.jsexpress或者使用node.jspm2搭建服务器&#xff0c;将vue或react打包后生成的dist目录在本地运行 vue项目打包后生成的dist目录如果直接在本地打开index.html,在浏览器中会报错&#xff0c;无法运行起来。 通常我是放到后端搭建的服务上面去运行&#xff0c;当时前端…

pve和openwrt以及我的电脑中网络的关系和互通组网

情况1 一台主机 有4个口&#xff0c;分别eth0,eth1,eth2,eth3 pve有管理口 这个情况下 &#xff0c;没有openwrt 直接电脑和pve管理口连在一起就能进pve管理界面 情况2 假设pve 的管理口味eth0 openwrt中桥接的是eth0 eth1 eth2 那么电脑连接eth3或者pve管理口设置eth3&#xf…

jmeter创建一个压测项目

1.jemeter新建一个项目&#xff1a; 2.接下来对Thread进行描述&#xff0c;也可以先使用默认的Thread进行操作。 3.添加http请求头的信息。按照如图所示操作 4.在请求头里面添加必要的字段&#xff0c;可以只填必要字段就可以 5.添加Http请求信息&#xff0c;如下图&#xff…

【webpack】动态配置cdn,多模板入口项目搭建

动态配置多模板 按照这个模板创建项目 安装glob,获取目录下的文件名称 封装方法utilsConfig&#xff0c;动态生产 page // pages 多入口配置 const path require("path");// glob 是 webpack 安装时依赖的一个第三方模块&#xff0c;该模块允许你使用 * 等符号, …