Java基础--->并发部分(1)

news/2024/5/9 9:53:26/文章来源:https://blog.csdn.net/weixin_56781779/article/details/130391342

文章目录

  • 线程基本概念
  • 线程的创建方式
  • 线程调度-------常用的方法
  • 线程的生命周期和状态
  • 并发编程的根本原因
  • Java内存模型(JMM)
  • 多线程核心的根本问题
  • volatile关键字
  • 保障原子性
  • synchronized和ReentrantLock的区别

线程基本概念

​ 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序就是一个进程从创建,运行到消亡的过程。

​ 在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

  • 程序:静态的代码

并发:一个CPU同时执行多个任务,指两个或多个事件在同一个时间段内发生。比如:秒杀、多个人做同一件事。

并发编程:例如:买票,抢购,秒杀等场景,有大量的请求访问同一个资源。

​ 会出现线程安全的问题,所以需要通过编程来控制解决让多个线程依次访问资源,称为并发编程。

并行:多个CPU同时执行多个任务,指两个或多个时间在同一时刻发生(同时发生)。比如:多个人同时做不同的事。

进程:运行中的程序,被加载到内存中,是操作系统分配内存的基本单位,每个进程都有自己独立的一块内存空间,一个进程可以有多个线程(至少有一个)

线程:线程是程序处理的基本最小单位,是cpu执行的单元,是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

线程的创建方式

  • 继承Thread类

继承Thread类,重写run() 方法,在run() 方法中写入线程要执行的程序,并实例化自定义的线程子类,也就是创建线程对象,调用start() 方法启动线程

特点:编写简单,可直接操作线程,适用于单继承

public class MyThread extends Thread {@Overridepublic void run() {// 线程执行逻辑}
}// 创建并启动线程
MyThread myThread = new MyThread();
myThread.start();

  • 实现Runnable接口

实现Runnable接口,重写run() 方法,在run() 方法中写入线程要执行的程序,在创建 Thread 对象时将 Runnable 子类实例作为参数传递并调用 start 方法启动线程。

特点:避免单继承局限性,便于共享资源

public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行逻辑}
}// 创建并启动线程
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();

线程调度-------常用的方法

        Thread thread = new Thread(td,"自定义线程");//创建线程的名称thread.start();//启动线程thread.join();//这个线程运行完了之后运行其他的    thread.getName();//获取线程名称thread.setName();//设置线程的名称thread.setPriority(1);//设置线程优先级(1-10)thread.getPriority();//得到线程优先级       thread.sleep(200);//以指定的毫秒数暂停执行thread.currentThread();//返回对当前正在执行的线程对象的引用thread.yield();//让步thread.isAlive();//判断线程是否活着setDaemon(bool);//设置线程是否为守护线程。object.notify();//唤醒等待的线程object.wait(); /*wait()  必须在同步代码块中使用必须是使用同步锁对象调用wait()wait()调用后,锁会释放必须要通过其他线程来唤醒*/

线程的生命周期和状态

Java线程在循行的生命周期中的指定时刻只可能处于下面6种不同状态的其中一个状态:

  • new:初始状态,线程被创建出来但是没有调用start()
  • runnable:运行状态,线程被调用了start()等待运行的状态
  • blocked:阻塞状态,需要等待锁释放
  • waiting:等待状态,表示该线程需要等待其它线程做出一些特定动作(通知或中断)
  • time_waiting:等待超时状态,可以在指定的时间后自行返回而不是像waiting那样一直等待
  • terminated:终止状态,表示该线程已经运行完毕
  • 守护线程

并发编程的根本原因

并发编程的根本原因在于计算机处理器的性能增长已经不能再利用频率提升这样的传统方法来提高计算机性能。因此,提高计算机的性能的唯一方法就是增加计算机的处理器核心数和使用并行计算的方式来实现

单核CPU,线程是串行执行,操作系统中有一个叫任务调度器的组件,将CPU的时间片分给不同的线程使用,由于CPU在线程间的切换非常快,人类感觉是同时运行的。

总结为一句话就是:微观串行,宏观并行,一般会将这种线程轮流使用 CPU的做法称为并发,concurrent

多核 CPU下,每个核(core)都可以调度运行线程,这时候线程可以是并 行的。

Java内存模型(JMM)

Java内存模型(Java Memory Model,JMM)是一套规范,定义了多线程的程序在执行时,内存中的各个变量、对象以及执行顺序等行为。

Java内存模型的主要目的是解决在并发编程中,由于线程之间共享同一变量或对象可能导致不正确的计算结果的问题,例如线程安全问题、可见性问题等。

Java内存模型规定了所有变量都存储在主内存中,每个线程都有自己的工作内存,线程在执行时从主内存中读取变量值到工作内存中,执行结束之后将结果写会主内存,不能直接读写主内存中的变量。为了保证不同线程之间的内存可见性,Java内存模型定义了一些同步机制,比如volatile关键字、synchronized关键字、Lock等,这些机制可以防止多线程并发访问同一变量或者对象时出现的竞争问题和数据一致性问题。

总之,Java内存模型是Java并发编程中的重要概念,它规定了共享变量的可见性、易失性和有序性,帮助程序员编写正确、高效的多线程程序。

多线程核心的根本问题

不可见性

​ 多个线程分别同时对共享数据操作,彼此之间不可见,操作完成后写回主内存,有可能出现问题。一个线程对共享变量的修改,不能被另外一个线程立刻看到,如今的多核处理器,每个CPU内核都有自己的缓存,而缓存仅仅可以被自己所在的处理器内核可见,CPU缓存与内存的数据不容易保持一致。

无序性

无序性是指程序的执行顺序不能被预测,即多线程程序中线程的执行顺序不确定,可能出现多个线程交叉执行的情况。这个特性可以提高程序的并发性能,但也为编写并发程序带来了额外的复杂性。
  
在Java中,出现无序性的主要原因是线程之间的工作内存和主内存的数据不一致。Java内存模型规定,每个线程都有自己的工作内存,线程在执行时需要将共享变量从主内存中读取到工作内存中,数据修改后再将数据写回到主内存中。但是,可能存在线程读取了主内存中的旧值,或者其他线程对共享变量的修改对该线程不可见的情况,导致出现无序性问题。
  
为了解决无序性问题,Java提供了volatile关键字,保证变量对所有线程的可见性。使用volatile关键字修饰的共享变量的值在每次被线程访问时,都强制从主内存中读取。这意味着每次访问的值都是最新的,能够避免出现无序性问题。
  
另外,Java 1.5引入了java.util.concurrent包中提供的原子变量类(如AtomicInteger,AtomicLong等)可以使用其提供的CAS操作,完成保证操作的原子性和可见性的操作,它们是线程安全的,可以用来替代那些非原子性的操作。这些类不但保证了原子性,也支持非阻塞算法,避免了使用synchronized关键字导致的线程阻塞和等待。
  
最后还有一个解决无序性问题的方法是使用Java.util.concurrent包下的Lock接口,它提供了比synchronized关键字更细粒度的锁机制。Lock接口提供了多种锁类型,如可重入锁、公平锁、读写锁等,可以根据需要灵活地使用相应的锁类型。
  
综上所述,Java提供了多种方法来解决无序性问题,比如使用volatile,原子变量类,Lock接口等。在编写多线程程序时,需要根据具体情况选择合适的方式,保证程序的正确性和高并发性能。

非原子性:

​ 原子性是指一个操作是不可分割的,要么全部执行成功,要么全部不执行,中间不会出现被其他线程干扰的情况。在多线程编程中,如果涉及到多个线程并发访问同一资源时,并且这些操作需要保证原子性,否则会出现数据不一致的情况。

原子性是拒绝多线程交叉操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作

总结

​ 缓存(工作内存)导致可见性问题,编译(指令重排)优化带来有序性问题,线程切换带来原子性问题。缓存、线程、编译优化的目的和我们写并发程序的目的是相同的,都是提高程序安全性和性能

volatile关键字

当一个线程执行过程中,提取的数据都是从内存中提取的,当其他线程修改后,当前线程获取到的错误的数据。使用volatile关键字就可以解决这些问题,volatile关键字可以用于声明一个变量,它可以确保变量在每次使用时从内存中读取,而在写入时也会立即刷新到内存中。这就保证了可见性和禁止指令重排序优化。

volatile底层实现原理

volatile 修饰的变量在操作前,添加内存屏障,不让它的指令干扰

内存屏障是一种CPU指令,用于限制指令重排序和缓存到内存之间的顺序。内存屏障对程序的执行和优化都有显著影响,可以保证多线程下的程序正确性和一致性。比如,一条内存屏障指令可以禁止编译器和处理器将其后面的指令移到内存屏障指令之前。

​ volatile 修饰的变量添加内存屏障之外,还要通过缓存一致性协议(MESI)将数据写回到主内存,其他工作内存嗅探后,把自己工作内存数据过期,重新从主内存读取最新的数据

保障原子性

1. 通过加锁的方式,让线程互斥执行来保证一次只有一个线程对共享资源访问

2. 在Java中还提供一些原子类(JUC),在低并发情况下使用,是一种无锁实现,

在JUC(java.util.concurrent)中,你可以见到java.util.concurrent.atomic和java.util.concurrent.locks。
  
加锁是一种阻塞式方式实现,原子变量是非阻塞式方式实现

原子类的原子性是通过volatile+CAS实现原子操作的
AtomicInteger类中的value是有volatile关键字修饰的,这就保证了value的内存可见性
  
低并发情况使用AtomicInteger

3. 采用自旋思想
采用CAS机制(Compare-And-Swap)比较并交换

概述:将第一次获取的值存入工作内存作为期望值,对数据进行修改,然后写入主内存,写之前判断与主内存中的值是否一致,一致直接写,不一致更新为新的值。
特点

  • 不加锁,所有的线程都可以对共享数据操作
  • 适合低并发使用
  • 由于不加锁,其他线程不需要阻塞,效率高

缺点:大并发时,不停自旋判断,导致CPU占用率高

ABA问题

某个线程将内存值由A改为了B,再由B改为了A。当另外一个线程使用预期值进行判断时,与内存值相同,当前线程的CAS操作无法判断这个值是否发生过变化。

解决ABA问题可以设置版本号,每次操作改变版本号,可以进行判断

synchronized和ReentrantLock的区别

synchronizedReentrantLock 都可以用来实现 Java 中的线程同步。它们的作用类似,但是在用法和特性上还是有一些区别的。

  1. synchronized 是 Java 内置的关键字,可以修饰代码块和方法,自动获取锁、释放锁,可以避免因为锁的释放问题导致的死锁;而 ReentrantLock 是Java类,只能对某段代码进行修饰,需要手动进行锁的获取和释放。

  2. ReentrantLock 的灵活性更高,比如支持可重入锁、支持公平锁和非公平锁、支持多个条件变量等,而 synchronized 则相对简化,更加方便快捷。

  3. 多个线程争抢 synchronized 的锁时,其中一个线程拿到锁后,其他线程进入锁池等待,直到持有锁的线程释放锁,其他等待线程才能继续竞争锁。而 ReentrantLock 可以灵活地控制锁的公平性和非公平性,以及等待的顺序。

  4. synchronized 在底层是依赖于 JVM 实现的,而 ReentrantLock 是使用 java.util.concurrent 包提供的一种基于接口的可重入锁,这种可重入锁的性能比较优秀,适用于高并发场景。

    综上所述,ReentrantLock 更加灵活,支持更多的特性和操作,适用于复杂的场景;而 synchronized 更加简化,使用方便,适用于一些简单的场景。

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

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

相关文章

机器学习实战教程(十):逻辑回归

概述 逻辑回归(Logistic Regression)是一种用于解决二分类或多分类问题的统计学习方法。它以自变量线性组合的形式进行建模,并使用Sigmoid函数将结果映射到[0, 1]的值域内,表示样本属于某个类别的概率。 Logistic Regression是最…

STM32CubeMX配置I2C通讯

1.如上图所示点击New Project 2.如上图所示选择自己所开发的新品最后双击芯片型号 3.配置RCC,我的芯片使用的是外部高速晶振。这里如图所选。 4.配置一下串口 5.配置I2C 6.根据自己的硬件选择时钟源和主频 6.①填写项目名②选择项目路径③选择开发环境④获取代码 …

活动目录(Active Directory)安全审计

延迟响应变化的影响可能会使原本应该微不足道的颠簸滚雪球变成无法弥补的损害。这在 Windows Active Directory 环境中更为重要,因为这种延迟造成的损害可能会使组织损失数百万美元!在这种情况下,需要一个警惕的警报系统,该系统可…

云原生-k8s核心概念(pod,deploy,service,ingress,configmap,volume)

Gitee-k8s学习 云原生实战-kubernetes核心实战 namespace Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离 Pod Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。 De…

从零搭建MySQL监控平台(mysql-exporter+Prometheus+Grafana)

文章目录 一、软件安装二、 软件配置配置mysql_exporter配置prometheus配置Grafana 本文是我自己在Macbook上本地从零开始搭建一套MySQL监控平台,监控的也是我本机的MySQL,过程包括prometheus、mysql_exporter、Grafana的配置与下载。 一、软件安装 我是…

Python一行代码实现文件共享【内网穿透公网访问】

目录 1. 前言 2. 视频教程 3. 本地文件服务器搭建 3.1 python的安装和设置 3.2 cpolar的安装和注册 4. 本地文件服务器的发布 4.1 Cpolar云端设置 4.2 Cpolar本地设置 5. 公网访问测试 6. 结语 转载自内网穿透工具的文章:Python一行代码实现文件共享【内网…

践行公益担当|人情如故,爱心依旧

爱心助学 情暖童心 随着改革开放,少数民族地区发生了翻天覆地的变化,城乡经济持续发展,人民生活水平日益提高。但对于很多居住在偏远山区的民族自然村,由于山区的地形限制,自然生存环境恶劣,交通及文化、教…

Revit砌体排砖的几种方法对比

方法简介 传统砌体深化排砖是绘图者使用CAD 软件通过二维想象进行排布,在墙面转角、两面或多面墙相互咬砌的位置,门窗洞口过梁的位置,构造柱等位置由于二维图形的局限性很难观察出排布是否合理。然而复杂区域砌体排布若出错…

捷报连连丨小匠物联SILA第六届“智光杯”荣获两项跨界大奖

2023年4月26日,SILA第六届“智光杯”跨界奖项名单公布。 喜讯传来,小匠物联荣获SILA第六届“智光杯”跨界奖项-全屋智能及商用系统优秀新供应链奖、智能照明新锐优秀新供应链奖。 “智光杯”“智光杯”由上海浦东智能照明联合会(SILA&#xf…

23.4.25总结

复习了MYSQL数据库的主键和外键的知识: 在设计表时,可以通过外键这个按钮,更改Update rule(更新规则)和Delete rule(删除规则)。 外键行为 默认情况下是:NO ACTION、RESTRICT 两…

【22-23 春学期】人工智能基础--AI作业2-监督学习

【22-23 春学期】AI作业2-监督学习_HBU_David的博客-CSDN博客 用自己的语言,解释以下概念 1 结构风险最小化 2 正则化 3 线性回归 4 逻辑斯蒂回归 5 Sigmoid 与 SoftMax 函数 6 决策树 7 信息熵 条件熵 信息增益 8 线性判别分析 LDA 9 概率近似正确 PAC …

最新:机器学习在生态、环境经济学中的实践技术应用及论文写作

查看原文>>>最新:机器学习在生态、环境经济学中的实践技术应用及论文写作 目录 专题一、理论基础与软件介绍 专题二、数据的获取与整理 专题三、常用评价方法与相关软件详细教学(案例详解) 专题四、写作要点与案例的讲解 近年来…

新手必看:蓝牙耳机什么牌子的好用?2023年蓝牙耳机排名

上班通勤、健身运动、游戏娱乐都离不开蓝牙耳机,蓝牙耳机市场这几年逐渐饱和,涌现了大量的品牌,蓝牙耳机什么牌子的好用成为热议话题,新手们在挑选时会参考排行榜,小编接下来将盘点2023年蓝牙耳机排名。 ●JEET Air 2蓝…

01 背包 (二维 )

首先是我对背包问题的理解: 有一个背包可以放下 n kg,有一些物品,价值和重量一一对应,问题是,需要怎样才能使背包中的价值最大? 不同的规则对应不同的背包问题 01背包:每一个物品只能被放入一次…

gl-opendrive插件(车俩3D仿真模拟自动驾驶)

简介 本插件基于免费opendrive开源插件、Threejs和Webgl三维技术、vue前端框架,blender开源建模工具等进行二次开发。该插件由本人独立开发以及负责,目前处于demo阶段,功能还需待完善,由于开发仓促代码还需优化。 因此&#xff…

el-input-number 输入框添加单位

需求 使用 element-ui 的 InputNumber 控件,实现金额填写,需要在数字后面添加一个单位:元 实现效果 代码部分 <template><el-dialogclass="morendialog":title="(formData.id ? 修改 : 新增) + title":visi

webhub123 设计师好用的笔刷纹理网站收录​

整理了一些可以免费下载的好用的笔刷和纹理资源网站&#xff0c;收录到 webhub123 设计师好用的笔刷纹理网站收录​http://www.webhub123.com/#/home/detail?projectHashid31645930&ownerUserid21336964 收录效果如下&#xff0c;每个网站显示为一张图片&#xff0c;点击…

[ZJCTF 2019]EasyHeap-patchlibc-调试

1,三连 主要功能&#xff1a; 1、malloc申请chunk 2、修改chunk内容 3、free chunk 4、exit 堆题多看一个libc信息&#xff1a; 2,IDA分析 2.1、malloc申请chunk heaparray[i]&#xff1a;存放 chunk 的地址。read_input(heaparray[i], size)&#xff1a;向 chunk 写入 s…

TryHackMe-Mnemonic(boot2root)

Mnemonic I hope you have fun. 端口扫描 循例nmap FTP枚举 尝试anonymous Web枚举 进80 gobuster扫 对着webmasters再扫一下 对着backups继续扫 下载zip文件&#xff0c;发现有密码 zip2john john直接爆 查看note.txt, 给出了ftpuser hydra直接爆ftp 进到ftp 用wget下载所…

【数据库】事务的隔离级别以及实现原理

文章目录 前言一、事务什么是事务&#xff1f;事务的四大特性分别是 二、事务并发存在的问题脏读可重复读不可重复读幻读 三、以MYSQL数据库来分析四种隔离级别第一种隔离级别&#xff1a;Read uncommitted(读未提交)第二种隔离级别&#xff1a;Read committed(读提交)第三种隔…