最专业的网站建设组织/小程序运营推广公司

最专业的网站建设组织,小程序运营推广公司,网站的换肤功能怎么做,开设赌场罪建设网站本文仅供个人学习使用,参考资料:JMM(Java 内存模型)详解 | JavaGuide 线程基础概念 用户线程:由用户空间程序管理和调度的线程,运行在用户空间。 内核线程:由操作系统内核管理和调度的线程&…

本文仅供个人学习使用,参考资料:JMM(Java 内存模型)详解 | JavaGuide 

线程基础概念

用户线程:由用户空间程序管理和调度的线程,运行在用户空间。

内核线程:由操作系统内核管理和调度的线程,运行在内核空间。

二者的区别和特点:用户献线程创建和切换成本低,但不可以利用多核,内核态线程,创建和切换成本高,可以利用多核。

jdk1.2之后的线程都是操作系统的线程,即基于原生线程实现(Native Threads)。

线程模型:一对一,多对一,多对多,这里就不说了。

在Windows和Linux中,java线程采用的都是一对一的模型。

线程和进程的区别:

线程是进程划分为更小的运行单位。线程和进程的最大的不同之处在于基本上各进程是独立的,而各线程不一定,同一进程中的线程极有可能会相互影响。线程执行开销小,但是不利于资源的管理和保护;而进程正相反。

创建线程的方式:严格来说,java只用一种方式可以创建线程:new Thread().start()

什么时候会发生线程的上下文切换?

  • 主动让出 CPU,比如调用了 sleep(), wait() 等。
  • 时间片用完。
  • 调用了阻塞类型的系统中断,比如请求 IO,线程被阻塞。
  • 被终止或结束运行

Thread.sleep()和Object.wait()方法对比

这两个方法都能暂停线程的执行,

  • Thread.sleep():定义在 Thread 类中,是一个静态方法。

  • Object.wait():定义在 Object 类中,是一个实例方法。

Thread.sleep()方法能让当前线程暂停执行指定的时间,进入(Timed_watiting状态)不释放任何锁资源,主要用于延迟执行或定时任务。

Object.wait()让当前线程进入等待状态,进入waiting或者Timed_waiting状态,必须持有对象的监视器锁*(即在synchronized块中调用),调用后会释放锁。主要用于线程间通信,等待其他线程通过notify()或者notifyAll()唤醒。

对于object.wait()的理解:

想象你和朋友合租,共用卫生间(共享资源):

  1. synchronized(lock):卫生间的门锁,一次只能一个人用。

  2. lock.wait():你进去后发现没纸了,于是出来并把钥匙挂回门口(释放锁),坐在沙发上等(等待)。

  3. lock.notify():室友买了纸后喊一声“有纸了!”,你听到后可以去抢钥匙。

这样设计保证了安全和效率!

可以直接调用Thread类中的run方法吗?

当直接执行run()方法中的内容时,会把run()方法当成一个main线程下普通方法,这并不是真正的多线程工作,只有new一个Thread,然后线程进入了新建状态。调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。

单核cpu支持java多线程吗?怎么实现的?

单核 CPU 是支持 Java 多线程的。操作系统通过时间片轮转的方式,将 CPU 的时间分配给不同的线程。尽管单核 CPU 一次只能执行一个任务,但通过快速在多个线程之间切换,可以让用户感觉多个任务是同时进行的。

os主要通过两种线程调度方式来管理多线程的执行:

  • 抢占式调度(Preemptive Scheduling):操作系统决定何时暂停当前正在运行的线程,并切换到另一个线程执行。(时间片轮转,公平性较好,cpu利用率高)
  • 协同式调度(Cooperative Scheduling):线程执行完毕后,主动通知系统切换到另一个线程。这种方式可以减少上下文切换带来的性能开销,但公平性较差,容易阻塞。

使用多线程可能带来什么问题?

内存泄露,死锁,线程不安全等等。

死锁的四个条件:

互斥条件,请求与保持条件,不剥夺条件,循环等待条件。

如何预防死锁?

破坏请求与保持:一次性申请所有资源

破坏不剥夺:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放

破坏循环等待:通过按序申请资源,释放资源则反序释放。

如何避免死锁?

银行家算法评估。

JAVA内存模型(JMM)

JMM主要定义了对于一个共享变量,当另一个线程对这个共享变量执行写操作后,这个线程对这个共享变量的可见性。

问题引出:

现代编译器在单线程下会对指令进行重排序来优化性能,但是没有义务保证多线程间的语义也一致。

常见的指令重排序有两种:

  • 编译器优化重排:编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
  • 指令并行重排:现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

对着两种重排序的处理方式也不一样:

  • 对于编译器,通过禁止特定类型的编译器重排序的方式来禁止重排序。

  • 对于处理器,通过插入内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)的方式来禁止特定类型的处理器重排序。

内存屏障是一种 CPU 指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障指令执行的有序性。另外,为了达到屏障的效果,它会在处理器写入值时,强制将写缓冲区中的数据刷新到主内存;在读取值之前,使处理器本地缓存中的相关数据失效,强制从主内存中加载最新值,从而保障变量的可见性。

什么是JMM?

可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,除了抽象了线程和主内存之间的关系之外,其还规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。

说白了就是定义了一些规范来解决这些问题,开发者可以利用这些规范更方便地开发多线程程序。对于 Java 开发者说,你不需要了解底层原理,直接使用并发相关的一些关键字和类(比如 volatilesynchronized、各种 Lock)即可开发出并发安全的程序。

java如何抽象线程和内存之间的关系

在现有的java内存模型下,线程可以把变量保存到本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写,这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

给出JMM的抽象示意图:

主内存vs本地内存

主内存(Main Memory)
  • 是什么:所有线程共享的内存区域,存储全局变量(如堆中的对象、静态变量等)。

  • 特点

    • 线程间共享,所有线程都能“看到”主内存中的数据。

    • 速度慢:访问主内存需要经过总线、缓存等,效率较低。

本地内存(Local Memory)
  • 是什么:每个线程独有的内存区域(实际是 JMM 的抽象概念,可能对应 CPU 缓存、寄存器等)。

  • 特点

    • 线程私有,其他线程无法直接访问。

    • 速度快:本地内存是线程的“工作副本”,用于缓存主内存中的数据。

    • 线程对变量的操作(读/写)优先在本地内存中进行,之后才会同步到主内存。

为什么会出现本地内存?

  • 性能优化:直接操作主内存太慢,本地内存(如 CPU 缓存)能大幅提升线程运行速度。

  • 副作用:本地内存的缓存机制会导致线程间数据不一致(需要开发者处理)。

然而由于二者的存在可能导致可见性和原子性的问题

可见性问题:

public class VisibilityProblem {private static boolean flag = true; // 主内存中的变量public static void main(String[] args) {// 线程 A:1秒后修改 flagnew Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}flag = false; // 修改后可能只更新了本地内存,未同步到主内存}).start();// 线程 B:循环检测 flagnew Thread(() -> {while (flag) { // 可能一直读取本地内存中的旧值// 空循环}System.out.println("线程 B 检测到 flag 已修改");}).start();}
}

结果可能:线程 B 永远无法退出循环,因为它读取的一直是自己本地内存中的旧值 flag = true

如何解决可见性问题:

方法 1:使用 volatile 关键字
  • 强制变量的读写直接操作主内存,跳过本地内存。

  • 修改示例代码:

    private static volatile boolean flag = true; // 添加 volatile
方法 2:使用 synchronized 同步块
  • 进入同步块时,会清空本地内存,从主内存重新加载变量。

  • 退出同步块时,会将本地内存的修改强制写回主内存。

并发编程的三个特性:

原子性:一次操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么都不执行。(synchronized锁和各种lock)

可见性:

当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。

在 Java 中,可以借助synchronizedvolatile 以及各种 Lock 实现可见性。

如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。

有序性:

由于指令重排序问题,代码的执行顺序未必就是编写代码时候的顺序。

我们上面讲重排序的时候也提到过:

指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致 ,所以在多线程下,指令重排序可能会导致一些问题。

在 Java 中,volatile 关键字可以禁止指令进行重排序优化。

Volatile关键字

如果一个变量被volatile关键字声明了,那么在java内存模型中读取它时就变成了这样的方式:

一个变量如果用volatile关键字修饰,就能保证数据的可见性,但是不能保证数据的原子性,synchronized关键字二者都能保证。

如何禁止指令重排序

在 Java 中,volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。 如果我们将变量声明为 volatile ,在对这个变量进行读写操作的时候,会通过插入特定的 内存屏障 的方式来禁止指令重排序。

在 Java 中,Unsafe 类提供了三个开箱即用的内存屏障相关的方法,屏蔽了操作系统底层的差异:

public native void loadFence();
public native void storeFence();
public native void fullFence();

通过这三个指令也能实现和volatile禁止重排序一样的效果,只是很麻烦。

这里给出一个例题,来自javaguide,同时也是我快手日常实习一面挂掉的一道题

解释并手写一下双重检验锁方式实现单例模式

代码:

public class Singleton{private volatile static Singleton uniqueInstance;private Singleton(){   }public static Singleton getUniqueInstance(){if(uniqueInstance==null){synchronized(Singleton.class){if(uniqueInstance == null){uniqueInstance =new Sngleton();}}}return uniqueInstance;}

uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

虽然这个volatile能保证多线程环境下变量的可见性,但是保证不了变量的原子性,只有synchronized锁和ReentranLock锁才能保证原子性和可见性。

悲观锁和乐观锁

悲观锁:每次获取资源都要上锁,其他线程想要拿到资源就要阻塞,直至锁被释放。共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程。

典型代表:synchronized和ReentranLock锁。

问题:高并发的场景下激烈的锁竞争会造成线程阻塞,导致频繁的上下文切换,增加系统的性能开销。还存在死锁问题。

乐观锁:乐观锁不加锁,不停的执行,只是在提交修改的时候去验证对应的资源是否被其他线程修改了

典型代表:版本号机制或者CAS算法

在 Java 中java.util.concurrent.atomic包下面的原子变量类(比如AtomicIntegerLongAdder)就是使用了乐观锁的一种实现方式 CAS 实现的。

问题:乐观锁相对悲观锁不存在锁竞争造成阻塞的问题,但是在写占比非常多的时候,会频繁的失败和重试,这样也会非常影响性能。

综上,悲观锁适用于写比较多的情况,乐观锁适用于读比较多的情况。

乐观锁的实现方式:

版本号机制和CAS算法(java并没有直接实现CAS,CAS相关的实现是通过C++内联汇编的形式实现的,JNI调用)

sun.misc包下的Unsafe类提供了compareAndSwapObjectcompareAndSwapIntcompareAndSwapLong方法来实现的对Objectintlong类型的 CAS 操作

Java中如何实现CAS?

使用Unsafe类,AtomicInteger的底层实现就是利用了Unsafe类提供的方法。

CAS操作可能会因为并发冲突而失败,因此通常会与while循环搭配使用,在失败后不断重试,直到操作成功。这就是 自旋锁机制 。

CAS的ABA问题怎么解决?

ABA问题的解决思路是在变量前面追加上版本号或者时间戳。

如果版本号和预期值都相等,那么就可以更新。

CAS的缺点:

CAS会采用自旋操作来进行重试,也就是不成功就一直循环直到执行成功,会带来很大的CPU开销

CAS操作只对单个共享变量有效,当需要操作多个共享变量时,CAS就无能为力,jdk1.5开始提供了AtomicReference类,通过将多个变量封装在一个对象中,我们可以使用AtomicReference来执行 CAS 操作。

除了 AtomicReference 这种方式之外,还可以利用加锁来保证。

synchronized关键字


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

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

相关文章

遗传算法+四模型+双向网络!GA-CNN-BiLSTM-Attention系列四模型多变量时序预测

遗传算法四模型双向网络!GA-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 遗传算法四模型双向网络!GA-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于GA-CNN-BiLSTM-Attention、CNN-BiL…

【新能源汽车“心脏”赋能:三电系统研发、测试与应用匹配的恒压恒流源技术秘籍】

新能源汽车“心脏”赋能:三电系统研发、测试与应用匹配的恒压恒流源技术秘籍 在新能源汽车蓬勃发展的浪潮中,三电系统(电池、电机、电控)无疑是其核心驱动力。而恒压源与恒流源,作为电源管理的关键要素,在…

在线JSON格式校验工具站

在线JSON校验格式化工具(Be JSON)在线,JSON,JSON 校验,格式化,xml转json 工具,在线工具,json视图,可视化,程序,服务器,域名注册,正则表达式,测试,在线json格式化工具,json 格式化,json格式化工具,json字符串格式化,json 在线查看器,json在线,json 在线验…

图片黑白处理软件推荐

图片黑白二值化是一款小巧实用的图片处理软件,软件大小仅268K。 它的操作极其简单,用户只需将需要处理的图片直接拖入软件,就能实现图片漂白效果。 从原图和处理后的图片对比来看,效果显著。这种图片漂白处理在打印时能节省墨水&a…

【AI知识】常见的优化器及其原理:梯度下降、动量梯度下降、AdaGrad、RMSProp、Adam、AdamW

常见的优化器 梯度下降(Gradient Descent, GD)局部最小值、全局最小值和鞍点凸函数和非凸函数动量梯度下降(Momentum)自适应学习率优化器AdaGrad(Adaptive Gradient Algorithm)​RMSProp(Root M…

1.5.5 掌握Scala内建控制结构 - 异常处理

本次实战聚焦于Scala内建控制结构中的异常处理机制。通过具体案例演示了如何使用try-catch-finally结构来处理程序运行中可能出现的异常情况。在try块中调用可能抛出异常的方法,catch块则根据不同异常类型进行捕获并处理,finally块则无论是否发生异常都会…

齿轮热处理学习笔记分享

对于一个做冷加工的人来说,热处理是一个神秘的话题,但是一点都不去了解的话,工作也无法进行。所以抽点时间来学习一下齿轮热处理相关的内容,做成笔记分享给爱学习的小伙伴们,文章较长,需要一些耐心去阅读&a…

Linux中vscode编程,小白入门喂饭级教程

确保Ubuntu联网 因为后面安装VScode需要从互联网下载。 安装GCC 在桌面空白处右键->打开终端 执行命令:gcc -v 在最后一行可以看到gcc version 7.5.0 如果提示Command ‘gcc’ not found,就查一下如何安装gcc,先把gcc安装好。 安装VS…

蓝桥杯真题——洛谷Day13 找规律(修建灌木)、字符串(乘法表)、队列(球票)

目录 找规律 P8781 [蓝桥杯 2022 省 B] 修剪灌木 字符串 P8723 [蓝桥杯 2020 省 AB3] 乘法表 队列 P8641 [蓝桥杯 2016 国 C] 赢球票 找规律 P8781 [蓝桥杯 2022 省 B] 修剪灌木 思路:对某个特定的点来说有向前和向后的情况,即有向前再返回到该位置…

matrix-breakout-2-morpheus 靶机----练习攻略 【仅获取shell】

【此练习仅做到反弹shell】 1.靶机下载地址 https://download.vulnhub.com/matrix-breakout/matrix-breakout-2-morpheus.ova 2. 打开靶机,kali使用nmap扫描同C段的主机 找到靶机ip 确保靶机和kali网卡均为NAT模式 先查看kali的ip nmap 192.168.182.1/24 …

Flutter中Align的使用说明

又失业了,作为一个高龄Android程序员今年找工作真难呀。现在Flutter是必需技能了,所以最近在自学。所用书籍叫《Flutter实战》,如下 如今已看了100多页,发现这本书写得……有点赶吧,好几处讲得不清不楚,而关…

用ASCII字符转化图片

代码 from PIL import Image# 定义 ASCII 字符集,从最暗到最亮 ASCII_CHARS "%#*-:. "def resize_image(image, new_width100):width, height image.sizeratio height / widthnew_height int(new_width * ratio)resized_image image.resize((new_wi…

详解Sympy:符号计算利器

Sympy是一个专注于符号数学计算的数学工具,使得用户可以轻松地进行复杂的符号运算,如求解方程、求导数、积分、级数展开、矩阵运算等。其中比较流行的深度学习框架pytorch的用到了Sympy,主要用于将模型的计算图转换为符号化表达式,以便进行分…

高频SQL 50 题(持续更新)

SQL的编写与运用 0. 写在前面 最近学习了数据库系统概论,其中涉及到了关于SQL语句的编写,感觉理论知识不足以让我掌握相关的编写方式,因此选择刷力扣上的题目进行复习巩固。 时间不是很多,可能不会经常更新,有时间写…

Unity插件-适用于画面传输的FMETP STREAM使用方法(三)基础使用

目录 一、插件介绍 二、组件介绍 三、Game View Streaming 1、使用 FM Network UDP 的基本设置 Server Scene Client Scene 2、使用使用 FM WebSocket 的基本设置 四、Audio Streaming 五、Microphone Streaming 一、插件介绍 ​​​​​​Unity插件-适用于画面传输的…

UI设计中的加载动画:优化用户体验的细节

hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 在数字产品泛滥的今天,用户对体验的要求早已超越功能本身。一个看似简单的加载动画&…

SpringBoot3+Vue3实战(Vue3快速开发登录注册页面并对接后端接口)(4)

目录 一、SpringBoot3Vue3实现基本增删改查。前后端通信交互、配置后端跨域请求。数据批量删除。(博客链接) 二、SpringBoot3Vue3快速开发登录、注册页面并实现对接。 &#xff08;1&#xff09;操作数据表employee(员工信息表)。 <1>修改employee表的字段组成。 <2&g…

Excel(函数篇):IF函数、FREQUNCY函数、截取函数、文本处理函数、日期函数、常用函数详解

目录 IF函数等于判断区间判断与AND函数、OR函数一同使用IFNA函数和IFERROR函数 FREQUNCY函数、分断统计LEFT、RIGHT、MID截取函数FIND函数、LEN函数SUBSTITUTE函数ASC函数、WIDECHAR函数实战&#xff1a;如何获取到表中所有工作簿名称文本处理函数TEXT函数TEXTJOIN函数 日期函数…

生成PDF文件:从html2canvas和jsPdf渲染到Puppeteer矢量图

刚刚实现而已&#xff1a;第一次明白&#xff0c;双击或file:///打开html文件&#xff0c;居然和从localhost:3000打开同一个html文件有本质的区别。 字体居然还能以Base64代码嵌入到网页&#xff0c;只是太大太笨。 需要安装node.js&#xff0c;npm安装更多依赖&#xff1a;…

Modbus TCP到RTU:轻松转换指南!

Modbus TCP 到 RTU&#xff1a;轻松转换指南&#xff01; 在现代工业自动化领域&#xff0c;Modbus TCP和Modbus RTU两种通信协议因其高效、稳定的特点被广泛应用。然而&#xff0c;随着技术的发展和设备升级的需求&#xff0c;经常会遇到需要将这两种协议进行互相转换的场景。…