Java 多线程知识

2023-05-16

参考链接:https://www.cnblogs.com/kingsleylam/p/6014441.html
https://blog.csdn.net/ly0724ok/article/details/117030234/
https://blog.csdn.net/jiayibingdong/article/details/124674922

导致Java线程安全问题最主要的原因:
(1)多线程同时访问共享数据。
(2)多线程操作共享数据的过程中使用的计算方法不具备原子性。
解决线程安全问题的方案:
(1)避免共享数据。
(2)确保使用共享数据的原子性。
避免数据共享:
JAVA虚拟机在内存管理过程中将内存划分为不同区域,其中类成员变量存储在堆内存,方法变量存储在栈内存。堆内存在不同线程之间共享数据,有线程安全问题,而栈内存是线程独占的内存,不存在线程安全线问题。在允许的情况下,不使用成员变量、而是用方法变量、临时变量的话,可以避免共享数据,从而确保数据的线程安全问题。

public class A
{
	private int account = 0;
	public void cal()
	{
		for(int i=0;i<100;i++)
		{
			acount++;
		}
	}
	public int getAccount()
	{
		return account;
	}
}

多线程并发的情况下,account 有线程安全问题,而变量i是线程安全的、没有线程安全问题。
JMM内存模型: 描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
在Java内存模型中:
(1)所有变量都存储在主内存中
(2)每个线程都有自己独立的工作内存,里面保存该线程使用到的变量副本。(主内存中该变量的一份拷贝)
(3)线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中进行读写。
(4)线程之间无法访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
共享变量实现可见性,必须经过如下两个步骤:
(1)线程中工作内存中的共享变量如果更新,需要将更新过后共享变量刷新到主内存中。
(2)主内存将最新的共享变量更新到其他工作内存中。
在这里插入图片描述
上图体现出线程只能与工作内存交互,不能直接访问主内存。当主内存中有一个共享变量X,则工作内存将X拷贝,线程操作工作内存中的X副本。

共享变量: 一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。
原子性: 指一个操作不可被中断,要么全部执行成功,要么全部执行失败。同一时刻只能有一个线程对它进行操作。
可见性: 一个线程对共享变量值的修改能够及时被其他线程看到。

指令重排序:处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中的各个语句的执行先后顺序同代码中的顺序一致,但它会保证程序最终执行结果和代码顺序执行的结果是一致的。
内存屏障: 用来禁止指令重排序。内存屏障分为两种:Load Barrier 和 Store Barrier,即读屏障和写屏障。作用: (1)阻止屏障两侧的指令重排序,即屏障下面的代码不能和屏障上面的代码交换顺序。(2)在有内存屏障的地方,线程修改完共享变量以后会马上把该变量从本地内存写回到主内存,并且让其他线程本地内存中该变量副本失效。
对于Load Barrier来说,在指令前插入
Load Barrier
,可以让高速缓存中的数据失效,强制从新从主内存加载数据;
对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,其他线程程可见

synchrnized

具有 原子性、可见性

对象锁(monitor)机制

进入同步代码块之前首先执行monitorenter指令,退出同步代码块执行monitorexit指令。当线程获得monitor后才能继续往下执行,否则就只能等待。
任何一个对象都有一个自己的monitor,线程执行对象的同步方法或同步块时,执行方法的线程必须获取该对象的monitor才能进入同步块和同步方法,若获取不到,则进入阻塞状态,进入同步队列。线程释放锁的时候会将值刷新到主内存中,其他线程获取锁时会强制从主内存中获取最新的值。——happen-before。类似线程通信。
**锁的重入性:**在同一锁程中,线程不需要再次获取同一把锁。synchrnized先天具有重入性。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加1,释放锁后就会将计数器减1。

CAS操作

悲观锁: 假设每一次执行临界区代码都会发生冲突,所以当前线程获取锁的过程会阻塞其他线程获取该锁。
乐观锁: 假设每一次执行临界区代码都不会发生冲突,所以不会阻塞其他线程操作。因此线程就不会出现阻塞停顿的状态。
CAS是用来鉴别乐观锁是否出现冲突,出现冲突就重试当前操作直到没有冲突为止。

CAS操作过程

CAS包含三个值,分别为:V内存地址存放的实际值、O预期的值、N更新的新值。当V和O相同时,表明该值没有被其他线程更改过,可以把新值N赋值给V。相反,V和O不相同,表明该值已经被其他线程改过了,则该旧值不是最新版本的值了,所以不能将新值N赋值给V,返回V即可。当多个线程使用CAS操作一个变量时,只有一个线程会成功,并成功更新,其余会失败。失败的线程会重新尝试,当然也可以选择挂起线程。

CAS的问题

**ABA问题:**因为CAS会检查旧值有没有变化,这里存在这样一个有意思的问题。比如一个旧值A变为了成B,然后再变成A,刚好在做CAS时检查发现旧值并没有变化依然为A,但是实际上的确发生了变化。解决方案可以沿袭数据库中常用的乐观锁方式,添加一个版本号可以解决。原来的变化路径A->B->A就变成了1A->2B->3C。
**自旋时间过长:**使用CAS时非阻塞同步,也就是说不会将线程挂起,会自旋(无非就是一个死循环)进行下一次尝试,如果这里自旋时间过长对性能是很大的消耗。
**只能保证一个共享变量的原子操作:**当对一个共享变量执行操作时CAS能保证其原子性,如果对多个共享变量进行操作,CAS就不能保证其原子性。有一个解决方案是利用对象整合多个共享变量,即一个类中的成员变量就是这几个共享变量。然后将这个对象做CAS操作就可以保证其原子性。atomic中提供了AtomicReference来保证引用对象之间的原子性。

voliate

轻量级同步机制。
voliate 是java中的一个关键字,用于修饰变量,被修饰的变量标记为线程共享,编译与运行时都会检查该变量,不会对其进行重排序。保证可见性有序性,不保证原子性
被voliate修饰的变量在编译成字节码时会多个lock前缀指令,该指令在执行过程中生成一个内存屏障,保证充排序后的指令不会越过内存屏障。即volatile之前的代码只会在volatile之前执行,volatile之后的代码只会在volatile之后执行。
满足以下一点,volatile 修饰的共享变量不加锁也能保证线程安全: 运算结果不依赖共享变量的当前值。(i++反例)。

Semaphore

继承关系
在这里插入图片描述
Semaphore用于管理信号量,在并发编程中可以控制访问同步代码的线程数量。

线程池

线程池是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后再创建线程后自动启动这些任务。
熟知的线程池: JDBC、数据库连接池(DataSource)、String(字符串常量池)

线程池的作用

  1. 降低资源消耗,通过重复利用已创建的线程降低创建和销毁的损耗。
  2. 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的基本原理

一个池子里面有很多线程,当再次需要执行某个任务时,不需要再创建线程了,而是直接从线程池中取出一个现成的线程供使用,即使该线程完成了任务,也不销毁线程,而是继续呆在线程池里准备迎接下一个任务。

为什么从池子中取要比创建线程快?

创建线程是在操作系统内核中完成的,涉及从用户态向内核态切换的操作,这个操作需要一定的开销。应用程序创建线程的是需要通过系统调用来完成的,进入操作系统内核中执行,也就是说,线程本质上就是PCB,是内核中的数据结构。创建线程是在内核中完成的,需要经历用户态->内核态的转变,而从线程池中取线程,把线程放回线程池,这一套是纯用户态的逻辑。

线程池主要参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
							  ThreadFactory threadFactory,
                              
                              RejectedExecutionHandler handler)
参数解释
corePoolSize线程池中的核心线程数。即使这些线程处于空闲状态也不会被销毁。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSIze,如果没有达到的话,会创建一个新线程来处理这个任务。
maximumPoolSize线程池允许的最大线程个数;当线程数达到corePoolSize后,如果有任务继续提交到线程池,会将任务缓存到工作队列中。如果队列也已满,则会创建一个新线程来进行处理。线程池不会无限制地去创建新线程,它会有一个最大线程数限制,这个数量由maximumPoolSize限制。
keepAliveTime空闲线程存活时间。一个线程如果处于空闲状态,并且当前线程数大于corePoolSize ,那么在keepAliveTime 后,这个空闲线程会被销毁。
unit空闲线程存活时间单位,keepAliveTime 的计量单位。
workQueue工作队列。新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk提供了四种工作队列:ArrayBlockingQueue、LinkedBlockingQuene、SynchronousQuene、PriorityBlockingQueue。
threadFactory创建一个新线程时使用的工厂,可以用来设定线程名,是否为daemon线程等等。
headler拒绝策略。当工作队列中的任务已经到达最大限制且线程池中的线程数也达到最大限制,这时有新任务再次提交会触发拒绝策略。jdk提供了4种拒绝策略:CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy。

**workQueue工作队列 **

  1. ArrayBlockingQueue 基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
  2. LinkedBlockingQuene 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
  3. SynchronousQuene 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
  4. PriorityBlockingQueue 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

handler 拒绝策略

  1. CallerRunsPolicy 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
  2. AbortPolicy 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
  3. DiscardPolicy 该策略下,直接丢弃任务,什么都不做。
  4. DiscardOldestPolicy 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列。

线程池如何确定线程数量

在拥有N个处理器的系统上,如何确定线程数量。需要确定任务是CPU密集型应用还是IO密集型应用,还是混合型应用。如果是CPU密集型应用,则线程池大小设置为N+1。如果是IO密集型应用,则线程池大小设置为2N+1。
实际开发处理方案是需要实验验证的。针对自己的程序进行性能测试,对线程池设置不同的数目:0.5N、N、1.5N、2N… 然后分别记录每种情况下程序的一些核心性能指标和系统负载情况,最后选择一个合适的配置。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 多线程知识 的相关文章

  • SQL语句练习题

    一 表结构 创建数据库 school 脚本 drop database school create database school 创建数据库 go CREATE DATABASE school ON NAME 61 school dat
  • Python+Tesseract-OCR实现图像中的文字识别

    开发环境 xff1a 运行平台 win10 编程语言 xff1a python IDE pycharm 三方库模块 xff1a pytessseract Tesseract OCR pytesseract库安装 xff1a 打开cmd xf
  • Ubuntu上网功能的配置

    虽然用了Ubuntu系统时间不短了 xff0c 但每次重装或者升级后都会出现无法上网的问题 xff0c 所以在此做下记录 xff0c 方便以后配置 xff0c 同时也希望能给不会配置的朋友一些帮助 网络配置流程 xff1a 前提准备工作 x
  • you-get使用命令

    安装 xff1a pip install you get you get支持的网站 xff1a 网站URL哔哩哔哩http www bilibili com 优酷http www youku com 腾讯视频http v qq com 爱奇
  • 【OpenVINO】 Windows10环境下载安装

    OpenVINO官方中文文档 xff1a OpenVINO 工具套件概述 OpenVINO 工具套件 https docs openvino ai cn latest index html OpenVINO下载地址 xff1a Downlo
  • 《计算机应用研究》投稿经历和时间节点

    记录四川计算机研究院 计算机应用研究 期刊投稿经历和时间节点 日期状态周期2022 11 09上传稿件当天显示编辑部已接收稿件 xff0c 开始初审2022 11 09 2022 11 15初审6天2022 11 15 2022 12 21
  • 项目管理工具:GitHub,GitLab,Azure DevOps,Gitea版本控制系统

    1 版本控制系统是什么 xff1f 版本控制系统是一种记录一个或若干文件内容变化 xff0c 方便查阅特定版本修订情况的系统 2 为什么要用版本控制系统 xff1f 工作上 xff0c 当你处理一个共享文件夹的时候 xff0c 必须告知办公
  • Linux Debian安装或管理多个Python版本

    在Debian安装或管理多个Python版本 2021 05 13 19 04 55 43 08 字数 xff1a 4772 标签 xff1a Linux Python Ubuntu 20 04的Python默认版本是3 8 xff0c 符
  • Fl Studio20切换中文教程汉化补丁包

    FL studio 简称FL xff0c 全称 Fruity Loops Studio xff0c 因此国人习惯叫它 34 水果 34 目前最新版本是FL studio20 xff0c 它让你的计算机就像是全功能的录音室 xff0c 大混音
  • Promox VE(PVE)重启后GUI不能登录

    ssh能连接 xff0c 但是gui打不开 因为集群节点不正确移除 xff0c 留下配置信息 xff0c 启动后 xff0c 一直找集群的其它机器 ssh远程指令 xff1a pvecm expected 1 上面的命令是告诉系统 xff0
  • 虚拟机已经显示了已连接的图标但不能上网的解决办法+虚拟机显示网络连接激活失败

    虚拟机已经显示了已连接的图标但不能上网的解决办法 43 虚拟机显示网络连接激活失败 问题叙述解决办法 问题叙述 解决办法 1 https blog csdn net big rotor article details 70163858 用w
  • 【递归】CodeForces - 768B

    题意 xff1a 给定一个数N xff0c 对大于1 的数在原来的位置拆分为N 2 xff0c N 2 xff0c N 2 三个数 对拆分出的大于1 的数同样进行拆分 xff0c 直至所有的数均为0 或1 对拆分后的0 1 序列 xff0c
  • 1025. 除数博弈

    2020 7 24 LeetCode 题目描述 爱丽丝和鲍勃一起玩游戏 xff0c 他们轮流行动 爱丽丝先手开局 最初 xff0c 黑板上有一个数字 N 在每个玩家的回合 xff0c 玩家需要执行以下操作 xff1a 选出任一 x xff0
  • VMware安装Arch Linux

    目录 一 新建虚拟机二 安装系统1 下载镜像2 将镜像文件导入空白虚拟机中3 查看启动模式4 查看网络5 查看系统时间6 分区6 1 查看磁盘信息6 2 进入图形化分区工具进行分区6 3 格式化分区6 4 挂载分区 7 修改镜像源8 安装l
  • HDU 1880 魔咒词典(Hash+二分)

    题目链接 哈利波特在魔法学校的必修课之一就是学习魔咒 据说魔法世界有100000种不同的魔咒 xff0c 哈利很难全部记住 xff0c 但是为了对抗强敌 xff0c 他必须在危急时刻能够调用任何一个需要的魔咒 xff0c 所以他需要你的帮助
  • Python「pytesseract」:中文识别模块

    在处理 ttf 文件时 xff0c 遇到了识别图片中中文的情况 xff0c 常见的方式是调用百度的语言识别接口 xff0c 但是这里为了大批量的识别 xff0c 首先试了试 python 自带的语言识别模块 pytesseract xff0
  • 总结rce(远程代码执行各种sao姿势)绕过bypass

    空格绕过 xff1a 在bash下可以用 IFS IFS IFS 9 09 lt gt lt gt xff08 例如 cat etc passwd xff09 20 space 09 tab xff0c 这儿跟sql注入有点相似 xff0c
  • docker-compose教程(安装,使用, 快速入门)

    此文章为转载文章 xff0c 原文地址 xff1a https blog csdn net pushiqiang article details 78682323 目录 1 Compose介绍2 Compose和Docker兼容性3 安装d
  • Ubuntu查看和设置环境变量

    文章目录 1 查看环境变量2 设置方式2 1 把你的路径加入PATH2 2 命名一个新的环境变量 3 作用域3 1 用于当前终端3 2 用于当前用户3 3 用于所有用户 转载 1 查看环境变量 查看环境变量有三个命令 xff1a env x
  • CorelDRAW2022下载附带序列号安装教程

    CorelDRAW2022作为图形设计软件的代表 xff0c 以其杰出和革新的特性赢得了长期的声誉和用户的赞赏 xff0c 是一套屡获殊荣的图像编辑软件 CorelDRAW 2022包含程序 xff1a CorelDRAW 2022主程序矢

随机推荐

  • ASCII码、Unicode编码对照表 —— ASCII控制字符 Unicode编码 字符编码的前世此生

    ASCII控制字符 Unicode编码 ASCII xff08 American Standard Code for Information Interchange xff0c 美国信息互换标准代码 xff0c ASC xff09 是基于拉
  • RealSR真实场景超分

    一 Camera Lens Super Resolution 本文主要解决RealSR的数据问题 xff0c 通过控制镜头到物体的距离产生成对的真实数据 xff08 Real paired SR data xff09 xff08 1 xff
  • 5374. 长度为 n 的开心字符串中字典序第 k 小的字符串(回溯算法)

    5374 长度为 n 的开心字符串中字典序第 k 小的字符串 List lt String gt res 答案集合不能定义为StringBuilder类型 剩下的就是回溯算法 span class token keyword class s
  • 宝塔忘记端口,解决办法

    1 xff0c 登陆远程连接 xff0c 输入ll 2 输入cd后再ll 3 清下屏 xff0c 输入clear 4 xff0c 输入cd www server panel data 找到port pl 5 输入cat port pl查看端
  • 幽冥传奇

    JAVA环境添加 setx M JAVA HOME D YM cnmmm com bl20166 Java jdk1 8 0 144 setx M CLASSPATH JAVA HOME lib dt jar JAVA HOME lib t
  • TOR下载教程

    不想用自己浏览器ip访问可用以下设置 xff0c 当然有很多其他方法 1 xff0c 官网https www torproject org 2 xff0c 下载对应版本 3 xff0c 打开tor 洋葱浏览器 并选择config 4 lin
  • 几步搞懂cobalt strike启动

    很多人问Cobalt strike怎么启动 xff0c 总结几句 1 cmd管理员身份运2 切换到CS所在目录3 输入ipconf找到自己ip地址4 输入teamserver 自己ip 密码 回车即可5 打开start bat文件再点击确定
  • TOR下载和网桥配置教程

    不想用自己浏览器ip访问可用以下设置 xff0c 当然有很多其他方法 1 xff0c 官网https www torproject org 2 xff0c 下载对应版本安装即可 本节以windows为例 xff08 苹果 安卓手机都有对应a
  • XSS漏洞,通过XSS实现网页挂马

    今天讲下通过XSS实现网页挂马 xff0c 目的是了解安全方面知识 xff0c 提升生活网络中辨别度 原理 xff1a 实验分为两部分 xff1a 1 通过Kali linux xff0c 利用MS14 064漏洞 xff0c 制作一个木马
  • qt样式有时不生效问题分析

    qt 中的样式表非常方便 xff0c 可以自定义自己想要的控件 但是有时候会发现使用样式表时 xff0c 样式不会生效 接下来分析一下主要原因 xff1a 1 样表格式不正确 2 样式表的样式被子对象覆盖 xff0c 设置时注意作用对象 x
  • 逻辑回归案例

    应用案例 之前学习了逻辑回归 xff0c 我们现在来做一个案例 一个图片验证码识别的案例 xff1a 怎么从图片中准确的识别出正确的数字 我们分了三步 第一步 xff1a 先生成150验证码图片 xff0c 每个图片有5个数字 图片中有随机
  • CorelDRAW x4提示非法软件产品被禁用解决方法教程

    说起PS大部分人都有所耳闻 xff0c 甚至会一些简单的操作 但是CDR x4这名字相信有很多人就很陌生了 xff0c 所以在这里也很有必要先说一下CDR到底是个什么样的存在 CDR全名CorelDRAW xff0c 是加拿大Corel公司
  • Mybatis-Plus中分页插件PaginationInterceptor, MybatisPlusInterceptor在SpringBoot中的使用

    配置分页插件 span class token annotation punctuation 64 Configuration span span class token keyword public span span class tok
  • 矩阵连乘问题-构造最优解

    题目描述 使用动态规划算法求解矩阵连乘问题 输入 每组数据包括两行 xff0c 第一行为数组长度n xff0c 第二行为存储矩阵维数的一维数组 输出 矩阵连乘最优计算次序 样例输入 Copy 7 30 35 15 5 10 20 25 样例
  • 树莓派启动——安装+无显示器使用+自启动VNC

    目录 硬件准备软件准备写入系统启动树莓派换源VNC自启动 时隔一年多 xff0c 拿起树莓派却忘记如何使用了 本想用作自己搭建git服务器 xff0c 后续再完成了 在此记录一下使用流程 硬件准备 树莓派 3b 43 TF卡和读卡器 xff
  • Debain 10(Buster)换源

    Debain 10 Buster 换源的操作步骤 必要条件 xff1a 已经安装好的Debain 10 Buster 开始 Debain 10 Buster 换源的操作步骤步骤一 备份原始的源文件用户切换到root下 进行源文件备份 二 换
  • 使用nginx反向代理突然失灵

    之前使用nginx反向代理还好好的 xff0c 后来再启动项目时突然失灵 xff0c 浏览器显示如下 然后开始排查错误 xff0c 首先直接使用ip地址访问是正常的 xff0c 然后使用hosts中映射的域名访问是无效的 xff0c 这说明
  • win10 安装 Linux子系统(WSL)

    序 xff1a 前段时间字节不是发布了 modernJS 的开源项目吗 xff1f 大概看了一部分的内容 xff0c 这些的东西就不一一列出来了 xff0c 本来想尝一口的 xff0c 在环境准备的系统那里就先折了一下 xff08 目前支持
  • Java 集合

    ArrayList 默认长度为10 indexOf lastIndexOf 通过equals方法判断索引 span class token keyword public span span class token keyword int s
  • Java 多线程知识

    参考链接 xff1a https www cnblogs com kingsleylam p 6014441 html https blog csdn net ly0724ok article details 117030234 https