多线程、进程、线程五种状态、synchronized、volatile、Lock、CAS、死锁、ThreadLocal

news2024/11/26 11:27:13

1、并发编程

并发编程三要素

  • 原子性:只一个操作要么全部成功,要么全部失败
  • 可见性:一个线程对共享变量的修改,其他线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行

synchronized,Lock解决原子性问题
volatile,,Lock解决可见性和有序性问题

2、线程和进程的区别

  • 根本区别:进程是操作系统资源分配的最小单位;线程是处理任务调度和执行的最小单位
  • 资源开销:每个进程都有单位的空间,进程间的切换有较大的开销;线程是轻量级的进程,同一类线程共享数据空间,每个线程有独立的运行栈和程序计数器,线程之间的切换开销较小
  • 包含关系:进程包含线程
  • 内存分配:线程共享地址空间和资源,进程之间的地址空间和资源相互独立
  • 影响关系:一个进程崩溃,在保护模式下其他进程不受影响;一个线程崩溃,其他线程也会受影响,整个进程都会崩溃。多进程要比多线程更加健壮
  • 执行关系:进程有独立的执行入口,线程不能独立执行,必须依赖进程

上下文切换:任务从保存到再加载的过程就是一次上下文切换。

3、创建线程的四种方式

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口
  • 使用Executors工具类创建线程池

Runnable和Callable区别

相同点:

  • 都是接口;
  • 都可以编写多线程程序;
  • 都采用Thread.start()启动线程

不同点

  • Runnable接口的run()方法没有返回值 ;Callable()接口的call()方法有返回值,是个泛型
  • Runnable接口run()方法只能抛出运行时异常,且无法捕捉处理;Callable接口call()方法允许抛出异常,可以获取异常信息

线程的run()和start()有何区别

  • start()用于启动线程,run()用于执行线程运行时的代码。run()方法可以重复调用,start()只能调用一次
  • start()方法用于启动线程,真正实现了多线程的运行。调用start()时无需等待run()方法方法体代码执行完毕就可以执行其他代码;此时线程是就绪态,并没有开始运行,然后通过Thread类调用run()方法完成其运行状态,run()方法运行结束,此线程就终止了
  • run()方法是在线程里的,直接调用run()方法,相当于调用了一个普通的函数,必须等待run()方法执行完毕才可以执行下面的代码,所以执行路径还是一条,没有多线程的特征,所以在线程启动时,要调用start()方法而不是run()方法。

Future和FutureTask

  • Callable接口的call()方法有返回值,Future可以拿到异步执行任务(这个任务也许并没有完成)的返回值,并且可以抛出异常信息。
  • FutureTask是Future的具体实现。FutureTask实现了RunnableFuture接口。RunnableFuture接口又同时继承了Future和 Runnable接口。所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

4、线程的状态和基本操作

线程的五种状态和生命周期

在这里插入图片描述

  • 新建(new)
  • 可运行/就绪态(runnable):调用start()方法后就处于Runnable态
  • 运行(running):runnable态获取到时间片,就进入running态;就绪态是进入运行态的唯一入口,线程要进入运行态就必须要进入就绪态
  • 阻塞(block)
  • 死亡(dead):死亡的线程不可复生

线程调度的方法

1、wait():使一个线程处于阻塞等待状态,并且释放所持有的对象锁
2、sleep():使役个正在运行的线程处于睡眠状态,是一个静态方法
3、notify():唤醒一个处于等待队列的线程,再调用此方法时,并不能确切的唤醒某个等待的线程,由JVM确定唤醒哪个线程,并且与优先级无关
4、notifyAll():唤醒所有处于等待队列的线程,然后重新竞争锁

wait()和sleep()区别

  • 所在类不同:sleep()是Thread类的静态方法;wait()是Object类的方法
  • 锁:sleep()不释放锁,wait()释放锁
  • 用途:wait()用于线程之间通信;sleep()用于暂停线程执行
  • 用法不同:wait()在结束后,不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()去唤醒;而sleep()方法在结束后,自动苏醒。

interrupt()、interrupted()、isInterrupted()

  • interrupt()、isInterrupted()是通过Thread对象调用,是实例方法;interrupted()是通过Thread类调用,是静态方法
  • interrupt()方法,只是通知该线程停止运行,只是通知,并没有直接中断,而是由程序自己决定是否中断

线程类的构造方法、静态代码块是被new这个线程的类的线程所调用的(谁new谁调用),run()方法是自身线程调用

5、synchronized

作用:用来控制线程同步的,被synchronized修饰的代码不能被多个线程同时执行,可以修饰类、方法、变量

synchronized底层原理

synchronized修饰的代码在反编译为字节码文件时,前后都出现了monitor字样,前面出现的是monitorenter,后面出现的是monitorexit,就是释放锁。当执行monitorenter时,当前线程试图获取对象锁所持有的monitor,当计数器为0时,就可以成功获取,当获取到时,计数器+1。并且就算当前线程已经拥有对象锁的monitor的持有权,那么就可以重入这个monitor,重入计数器也会加一。如果其他线程占有monitor持有权,那么当前线程就会阻塞,知道其他线程执行monitorexit,执行后锁释放,计数器设置为0。

自旋

即其他线程不进入阻塞态,而是在synchronized边界循环等待,不断尝试获取锁,这就是自旋

synchronized锁升级

synchronized涉及到用户态和内核态的切换,在1.6之前,锁都是重量级锁,即我们不管什么线程来操作资源,都要进行加锁释放锁,如果有多线程,还要等待之类的,很浪费资源,1.6之后引入了偏向锁与轻量锁来减小获取和释放锁所带来的性能消耗。

锁升级其实就是对synchronized的优化,以前用synchronized修饰一个对象或者是方法,方法也等于是锁住对象,直接用一把操作系统层面的大锁,万一只有少量线程的话会大题小作了,如果大量线程的话又会特别消耗时间,划不来,所以要将以前的二话不说用一把大锁进行优化。

无锁->偏向锁->轻量级锁->重量级锁

原理:在锁对象的对象头有一个threadid字段,第一次访问时threadid为空,JVM让其持有偏向锁,并把threadid设置为线程id,再次进入时只需要判断两个id是否相等,相等就直接进入,不相等就升级为轻量级锁;自旋一段时间后还没有获取到就升级到重量级锁。

synchronized、volatile、CAS区别

  • synchronized是悲观锁,属于抢占式,会引起其他线程阻塞
  • volatile提供多个线程共享变量可见性和禁止指令重排序
  • CAS是基于冲突监测的乐观锁(非阻塞)

synchronized、Lock、ReentrantLock区别

  • synchronized、ReentrantLock都是可重入锁
  • synchronized是关键字,Lock是接口,ReentrantLock是实现了Lock接口的一个类
  • synchronized可以给类、方法、代码块加锁,Lock和ReentrantLock只能给代码块加锁
  • synchronized不用手动获取和释放,发生异常会自动释放锁,不会造成死锁;Lock和ReentrantLock需要手动,没有unLock()就会死锁
  • Lock可以知道是否成功获取到锁,synchronized不行

6、volatile

保证可见性和禁止指令重排序,提供happens-before的保证,确保一个线程的修改对于其他线程是可见的。被volatile修饰的共享变量,当它被修改时,可以将修改的值立即更新到主内存中,其他线程需要读取时,重新去主内存中读取新值
volatile可以保证可见性和禁止重排序,但不能保证原子性;atomic方法可以让这种方法具有原子性

7、Lock体系

Lock是synchronized的扩展版本,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的锁操作。Lock的实现基本都支持公平锁和非公平锁,synchronized只支持非公平锁

  • 悲观锁:悲伤的假设最坏的情况,每次拿数据都认为别人会修改,所以在拿的时候就会加锁,别人想拿就阻塞(共享资源每次只给一个线程使用,其他线程阻塞,用完再把资源转让给其他线程)
  • 乐观锁:每次拿数据,不会上锁,直到提交数据时才会证实数据是否被修改(产生并发冲突),多用于多读场景。一般用版本号或者CAS实现

8、CAS

CompareAndSweep——比较并交换
CAS包含三个操作数——内存位置(V)、预期值(A)、拟写入的新值(B)

  • 第一步:比较V和A是否相等
  • 第二步:相等,就把B写入V
  • 第三步:返回boolean类型,表示操作成功

多个线程进行CAS操作时,只有一个线程可以操作成功,其他线程自旋等待

CAS产生的问题

  • ABA问题:从A变到B,再从B变到A,过程不知道;解决办法:引入版本号
  • 循环开销时间大:资源竞争严重时,CAS自旋概率大,浪费CPU
  • 只能保证一个共享变量的原子性操作

9、线程死锁

两个或以上的线程互相持有对方资源并且不主动释放造成的恶性循环

死锁的四个条件

  • 互斥条件:一个资源只能被一个线程占用
  • 请求与保持条件:请求被占用资源而阻塞,不放弃已经获得的资源
  • 不剥夺条件:资源未使用前不能被其他线程强行剥夺
  • 循环等待条件:等待的线程形成了一个死循环

避免死锁

破坏造成死锁四个条件中的一个就行

  • 互斥条件无法破坏
  • 破坏请求与保持条件:一次性申请所有资源
  • 破坏不剥夺条件:申请不到被占用的资源,就主动释放
  • 破坏循坏等待条件:

活锁

没有被阻塞,只是某些条件没满足,导致一直重复尝试、失败、尝试、失败这个过程
活锁有可能自己解开,死锁不能

饥饿

因为种种原因无法获取到所需要的资源,导致一直无法执行

10、ThreadLocal

为线程提供局部变量,保证各个线程里的变量独立于其他线程的变量,也就是说ThreadLocal为每个线程创建一个单独的副本,线程之间不相关,
同步机制是为了保证多线程环境下数据的统一性,而ThreadLocal则是保证多线程环境下数据的独立性

ThreadLocal底层原理

Thread类中有一个ThreadHashMap的数据结构,用来保存线程对象的变量
每个线程的ThreadHashMap都是属于线程自己的,这就保证了每个线程都是独立的,多个操作不会互相影响

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

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

相关文章

【HUST】信道编码|基于LDPC码的物理层安全编码方案概述

本文对方案的总结是靠 Kimi 阅读相关论文后生成的,我只看了标题和摘要感觉确实是这么回事,并没有阅读原文。 行文逻辑:是我自己设定的,但我并不是这个研究领域的,所以如果章节划分时有问题,期待指出&#x…

实验二-基于FPGA的VGA协议实现

目录 一、VGA介绍 原理 特征 二.VGA显示字符 三.VGA显示彩色条纹 四.总结 参考 任务要求: 1. 深入了解VGA协议,理解不同显示模式下的VGA控制时序参数(行频、场频、水平/垂直同步时钟周期、显示后沿/前沿等概念和计算方式)&…

蓝熊OZON数据分析,蓝熊OZON选品平台

在跨境电商的浪潮中,数据分析与选品策略成为了决定企业成功与否的关键因素。蓝熊OZON作为一家专注于跨境电商数据分析与选品平台,凭借其精准的数据分析能力与独特的选品模式,为众多电商企业提供了强有力的支持。接下来看看。 免费体验OZON选品…

别对我动心短视频:成都鼎茂宏升文化传媒公司

别对我动心短视频:时代的爱情哲学与心理探索 在短视频的海洋里,"别对我动心"这样的标题,如同一颗石子投入平静的湖面,激起了层层涟漪。它不仅仅是对一段情感的拒绝,更是一种现代人情感态度的表达&#xff0…

Go微服务开发框架DMicro的设计思路

DMicro是一个基于Go语言开发的微服务开发框架,旨在简化微服务架构的开发、部署和运维过程。DMicro的设计思路主要围绕以下几个方面展开: 简化微服务开发流程 DMicro通过提供一套简洁的API和工具,使得开发者可以快速搭建微服务应用。它支持服…

Java基础的语法---String

Java的String类是不可变的,意味着一旦创建,其值就不能被改变。String类提供了丰富的API来操作字符串。 以下是一些常用的方法: 构造方法: 有以下几种常见的: public class stringlearn {public static void main(S…

群晖NAS使用Docker部署WPS Office结 合内网穿透实现远程编辑本地文档

文章目录 1. 拉取WPS Office镜像2. 运行WPS Office镜像容器3. 本地访问WPS Office4. 群晖安装Cpolar5. 配置WPS Office远程地址6. 远程访问WPS Office小结 7. 固定公网地址 wps-office是一个在Linux服务器上部署WPS Office的镜像。它基于WPS Office的Linux版本,通过…

父进程等待子进程退出

一、 为什么要等待子进程退出? 等待子进程退出是为了确保父进程能够在子进程执行完毕后继续执行或者处理子进程的结果。在许多情况下,父进程需要等待子进程完成后才能继续执行,以确保正确的执行顺序和结果。 以下是一些等待子进程退出的主要…

chessbase的中文语言strings文件比较与生成工具

请支持我的店铺tao 宝 王后兵 店 把chessbase17或fritz19根目录(如C:\Program Files\ChessBase\CBase17)的messages文件夹复制到本py文件所在的文件夹,运行本py文件可以生成新的Chinese strings文件。 strings2.py """strin…

第52期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区,集成了生成预训练Transformer(GPT)、人工智能生成内容(AIGC)以及大语言模型(LLM)等安全领域应用的知识。在这里,您可以找…

Vitis HLS 学习笔记--基本指针和算术指针

目录 1. 简介 2. 基本指针 3. 算术指针 4. 疑点解答 4.1 疑点1 4.2 疑点2 5. 总结 1. 简介 在 C/C 语言中,指针被广泛用来表示内存中的地址信息,它们是理解和使用这些语言的核心概念之一。然而,在 Vitis HLS 中,指针的使用…

Unity射击游戏开发教程:(20)增加护盾强度

在本文中,我们将增强护盾,使其在受到超过 1 次攻击后才会被禁用。 Player 脚本具有 Shield PowerUp 方法,我们需要调整盾牌在被摧毁之前可以承受的数量,因此我们将声明一个 int 变量来设置盾牌可以承受的击中数量。

【大模型】fineturn Q-wen

github上下载qwen1_5源码 修改finetun.sh 然后在路径qwen1_5/examples/sft下修改finetun.sh, 内容如下 #!/bin/bash export CUDA_DEVICE_MAX_CONNECTIONS1 DIRpwd# Guide: # This script supports distributed training on multi-gpu workers (as well as single-worker trai…

大数据Hadoop之-工具HIVE(一)

大数据Hadoop之——数据仓库Hive HIVE介绍Hive是基于Hadoop的一个数据仓库(Data Aarehouse,简称数仓、DW),可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。是用于存储、分析、报告的数据系统。 在Hadoop生态系统中,HDFS用于存储数据,Yarn用于资源管理…

WSL调用docker

WSL(windows subsystem linux)是window系统的原生linux子系统,用于代码开发很方便。 希望在wsl里面运行docker,首先要安装docker在WSL中使用,大部分人的第一想法肯定是用以下命令行安装(个人不推荐&#x…

大语言模型本地部署与使用_ollama_open-webui

概述 本文主要记录如何使用ollama运行开源的大语言模型如llama3等,以及如何使用open-webui进行交互。 ollama支持MacOS、Linux、Windows等操作系统,这里主要以Linux和Windows为主,讲述如何在本地运行大语言模型。 一 安装ollama 1.1 Wind…

centos 8.5 yum 更换阿里云源

在CentOS 8上更换为阿里云源,步骤操作: 1 备份当前的yum源配置文件 cp -a /etc/yum.repos.d /etc/yum.repos.d.backup 2 清理原来 官方默认源 rm -rf /etc/yum.repos.d/*.repo 3 下载阿里云CentOS 8的yum源配置文件 curl -o /etc/yum.repos.d/CentOS-Base.rep…

桌面藏线大法

1有线改无线: 蓝牙鼠标 蓝牙键盘 蓝牙耳机 2将排插贴到桌子底下 购物软件上搜 3断舍离 不要的电子产品统统扔掉 4 洞洞板和挂钩 这个不用介绍了

由于找不到mfc140u.dll怎么办,介绍5种靠谱有效的解决方法

当您的电脑显示“mfc140u.dll丢失”的错误时,通常是因为系统中缺少了某个必要的动态链接库文件。这个问题可能会导致某些应用程序无法正常运行,给用户带来困扰。下面我将详细介绍解决该问题的五种方法。 一,关于mfc140u.dll文件的概述 mfc14…

如何在 ASP.NET Core 中实现中间件管道

概述:借助 ASP.NET Core,中间件流水线可以作为一种轻量级、灵活的机制,使开发人员能够在请求流水线的不同阶段插入功能。这些中间件组件可以执行各种任务,例如日志记录、身份验证、授权、异常处理等。它们提供了一种封装和组织代码的方法,促进了更简洁、更易于维护的应用程…