关于并发编程的一些总结

news2024/11/26 12:21:12

并发编程

1.synchronized是什么?

synchronized是Java中的一个关键字,主要是为了解决多个线程访问共享资源的同步性,可以保证被它修饰的代码块或方法在任何时间至多只有一个线程执行。

2.synchronized的进化史?

在早期Java版本中,synchronizd属于重量级锁,性能低下。

synchronized 在 JDK 1.6 之后引入了锁优化,可以随着多线程竞争激烈程度的不同而选择合适的锁策略。

  1. 当没有线程竞争的时候,是无锁的状态
  2. 当只有一个线程竞争的时候,是偏向锁
  3. 当有多个线程竞争的时候,撤销偏向锁,成为轻量级锁
  4. 当轻量级锁 CAS 次数太多的时候,就会撤销轻量级锁,称为重量级锁

轻量级锁和重量级锁的区别:

在轻量级锁下,线程是不进行阻塞的,线程拿不到锁会不断CAS自旋,直到拿到锁为止,这样会充分利用CPU资源,并在锁释放瞬间确保其他锁可以拿到,但是如果线程竞争激烈,线程就会不断CAS,这也是我们不想看到的。

重量级锁下,拿不到锁的线程会被阻塞,阻塞线程涉及到用户态到内核态的切换,这也是很大的消耗。

3.在高并发场景下,并发度肯定是比较高的,不建议使用 synchronized 的原因主要有以下几点:

  • 由于并发度比较高,因此 synchronized 一定会升级到重量级锁,但是重量级锁的性能是不太高的,因为线程要阻塞再唤醒,需要用户态和内核态之间切换
  • synchronized 没有读写锁优化
  • synchronized 不能对线程唤醒,也就是你线程如果获取不到锁的话会一直阻塞

4.synchronized怎么用?

  • 修饰实例方法,给当前对象实例加锁,要获得当前对象实例的锁
synchronized void method() {
    
}
  • 修饰静态方法

给当前类加锁,会作用于类的所有对象实例,进入同步代码块前要获得当前 class 的锁

这是因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。

synchronized static method() {
    
}
  • 修饰代码块(锁指定对象/类)
    • synchronized(object) 表示进入同步代码库前要获得 给定对象的锁
    • synchronized(类.class) 表示进入同步代码前要获得 给定 Class 的锁
synchronized(this) {
    // 业务代码
}

5.synchronized的实现原理

为什么说synchronized是基于对象实现的,先掌握Java对象在堆中的存储。

一个Java对象存储在堆上,分别是:对象头、对象实例数据、对齐填充。

对象头展开是:MarkWord、ClassMetadataAddress、数组长度

其中MarkWord记录了两部分信息:

  • 第一部分用于存储对象自身运行时数据,如哈希码、GC分代年龄
  • 另一部分存储了锁信息

进入同步代码块,调用monitorEnter方法,计数器加一,离开同步代码块,调用monitorExit方法,计数器减一

当计数器为0可以拿到锁,同时可以释放锁

6.线程中断与synchronized

//中断线程(实例方法)
public void Thread.interrupt();

//判断线程是否被中断(实例方法)
public boolean Thread.isInterrupted();

//判断是否被中断并清除当前中断状态(静态方法)
public static boolean Thread.interrupted();

7.volatile

在多线程并发编程中synchronized和volatile都扮演着重要的角色,volatile是轻量级的 synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程 修改一个共享变量时,另外一个线程能读到这个修改的值。如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。本文将深入分析在硬件层面上Intel处理器是如何实现volatile的,通过深入分析帮助我们正确地使用volatile变量。

volatile是Java当中的关键字

它的作用是禁止指令重排序和保证变量的可见性

但它不能保证原子性

  • 保证变量的可见性:当一个线程去修改被volatile修饰的变量时,可以保证修改之后的结果立刻刷新到主存上;当一个线程去读取被volatile修饰的变量时,必须强制从主存中读取,禁用缓存
  • 禁止指令重排序:防止JVM编译源码生成class时使用重排序

1)Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声 言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以 独占任何共享内存[2]。但是,在最近的处理器里,LOCK#信号一般不锁总线,而是锁缓存,毕 竟锁总线开销的比较大。在8.1.4节有详细说明锁定操作对处理器缓存的影响,对于Intel486和 Pentium处理器,在锁操作时,总是在总线上声言LOCK#信号。但在P6和目前的处理器中,如果 访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反,它会锁定这块内存区 域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁 定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。

2)一个处理器的缓存回写到内存会导致其他处理器的缓存无效。IA-32处理器和Intel 64处 理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致 性。在多核处理器系统中进行操作的时候,IA-32和Intel 64处理器能嗅探其他处理器访问系统 内存和它们的内部缓存。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的 缓存的数据在总线上保持一致。例如,在Pentium和P6 family处理器中,如果通过嗅探一个处理 器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理 器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充

8.ReentrantLock

ReentrantLock 是基于 AQS ,抽象同步队列实现的。

ReentrantLock基于AQS,在并发编程中它可以实现公平锁非公平锁来对共享资源进行同步

同时,和synchronized一样,ReentrantLock支持可重入,除此之外,ReentrantLock在调度上更灵活,支持更多丰富的功能

ReentrantLock的lock方法:

在进入lock方法后,发现内部调用了sync.lock()方法。

Sync这个类是一个抽象类,它有两个实现类,Sync继承了AQS

Sync类:
    abstract static class Sync extends AbstractQueuedSynchronizer {
        
NonFairSyncstatic final class NonfairSync extends Sync {
     
FairSyncstatic final class FairSync extends Sync {

使用公平锁,参数加true,非公平锁直接无参构造就行了:
        public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平锁:

获取锁时,看有没有线程排队,有排队就不去竞争,线程获取锁的顺序与入阻塞队列的顺序一致,不会有"插队"行为。

非公平锁:

不管有没有线程排队,先尝试获取锁资源,获取不到再去"排队",但是有"插队行为"。

分析AQS

Sync继承了AQS,我们来分析一下AQS

AQS的数据结构是一个双向链表/队列,它里面有个Node类

abstract static class Node {
        volatile Node prev;       // initially attached via casTail
        volatile Node next;       // visibly nonnull when signallable
        Thread waiter;            // visibly nonnull when enqueued
        volatile int status;      // written by owner, atomic bit ops by others

        // methods for atomic operations
        final boolean casPrev(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, PREV, c, v);
        }
        final boolean casNext(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, NEXT, c, v);
        }
        final int getAndUnsetStatus(int v) {     // for signalling
            return U.getAndBitwiseAndInt(this, STATUS, ~v);
        }
        final void setPrevRelaxed(Node p) {      // for off-queue assignment
            U.putReference(this, PREV, p);
        }
        final void setStatusRelaxed(int s) {     // for off-queue assignment
            U.putInt(this, STATUS, s);
        }
        final void clearStatus() {               // for reducing unneeded signals
            U.putIntOpaque(this, STATUS, 0);
        }

        private static final long STATUS
            = U.objectFieldOffset(Node.class, "status");
        private static final long NEXT
            = U.objectFieldOffset(Node.class, "next");
        private static final long PREV
            = U.objectFieldOffset(Node.class, "prev");
    }

在这里插入图片描述

ReentrantLock是如何拿到锁的?无论是公平还是非公平:

线程通过CAS的方式能够把AQS中的state变量从0变为1,就代表拿到锁了

9.让线程进入阻塞有哪些方法?

  1. 使用 Thread.sleep(long millis):通过调用 Thread.sleep() 方法,线程可以暂时挂起一段时间,进入阻塞状态。在指定的时间过后,线程会重新进入就绪状态。
  2. 使用 Object.wait():线程可以通过调用 Object.wait() 方法进入等待状态,等待其他线程调用相同对象的 notify()notifyAll() 方法来唤醒它。
  3. 使用 Thread.join():在一个线程中调用另一个线程的 join() 方法可以使调用线程等待被调用线程执行完毕,进入阻塞状态,直到被调用线程执行完毕。
  4. 调用 Lock 接口中的 lock 方法:通过 Lock 接口获取锁时,若锁已经被其他线程持有,线程将进入阻塞状态,直到获取到锁。
  5. 使用 BlockingQueueBlockingQueue 是一个用于线程间通信的阻塞队列,当队列为空或满时,线程在插入或获取元素时会被阻塞。
  6. I/O 操作:执行阻塞式的 I/O 操作(如读取文件、网络通信等)会使线程进入阻塞状态,直到操作完成。

这些方法允许线程进入阻塞状态,并且在满足特定条件或时间后,线程可以重新进入就绪状态,等待调度器再次分配执行。

10.控制并发线程数的Semaphore

《[中文]Java并发编程的艺术》

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程 并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用Semaphore来做流量控制,如代码所示。

public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorServicethreadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
	for (inti = 0; i< THREAD_COUNT; i++) {
		threadPool.execute(new Runnable() {
			@Override
			public void run() {
			try {
				s.acquire();
				System.out.println("save data");
				s.release();
				} catch (InterruptedException e) {
					}
						}
			});
		}
		threadPool.shutdown();
	}
}

在代码中,虽然有30个线程在执行,但是只允许10个并发执行。Semaphore的构造方法 Semaphore(int permits)接受一个整型的数字,表示可用的许可证数量。Semaphore(10)表示允 许10个线程获取许可证,也就是最大并发数是10。Semaphore的用法也很简单,首先线程使用 Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。

Semaphore还提供一些其他方法,具体如下。

**·intavailablePermits():**返回此信号量中当前可用的许可证数。

**·intgetQueueLength():**返回正在等待获取许可证的线程数。

**·booleanhasQueuedThreads():**是否有线程正在等待获取许可证。

**·void reducePermits(int reduction):**减少reduction个许可证,是个protected方法。

**·Collection getQueuedThreads():**返回所有等待获取许可证的线程集合,是个protected方 法

中当前可用的许可证数。

**·intgetQueueLength():**返回正在等待获取许可证的线程数。

**·booleanhasQueuedThreads():**是否有线程正在等待获取许可证。

**·void reducePermits(int reduction):**减少reduction个许可证,是个protected方法。

**·Collection getQueuedThreads():**返回所有等待获取许可证的线程集合,是个protected方 法

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

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

相关文章

Python合并两张图片 | 先叠透明度再合并 (附Demo)

目录 前言正文 前言 用在深度学习可增加噪音&#xff0c;增加数据集等 推荐阅读&#xff1a;Pytorch 图像增强 实现翻转裁剪色调等 附代码&#xff08;全&#xff09; 正文 使用Pillow库来处理图像&#xff08;以下两张图来自网络&#xff09; 图一&#xff1a; 图二&…

飞塔防火墙开局百篇——002.FortiGate上网配置——透明模式配置(Transparent)

透明模式配置 开启透明模式创建策略 在不改变现有网络拓扑前提下&#xff0c;将防火墙NGFW以透明模式部署到网络中&#xff0c;放在路由器和交换机之间&#xff0c;防火墙为透明模式&#xff0c;对内网网段192.168.1.0/24的上网进行4~7层的安全防护。 登陆FortiGate防火墙界面&…

Selenium自动化测试面试题全家桶

1、什么是自动化测试、自动化测试的优势是什么&#xff1f; 通过工具或脚本代替手工测试执行过程的测试都叫自动化测试。 自动化测试的优势&#xff1a; 1、减少回归测试成本 2、减少兼容性测试成本 3、提高测试反馈速度 4、提高测试覆盖率 5、让测试工程师做更有意义的…

2021年中国环境统计年鉴、工业企业污染排放数据库

《中国环境统计年鉴》是国家统计局和生态环境部及其他有关部委共同编辑完成的一本反映我国环境各领域基本情况的年度综合统计资料。收录了上一年年全国各省、自治区、直辖市环境各领域的基本数据和主要年份的全国主要环境统计数据。 内容共分为十二个部分,即:1.自然状况;2.水环…

收下这份实操案例,还怕不会用Jmeter接口测试工具

一、简介 JMeter&#xff0c;一个100&#xff05;的纯Java桌面应用&#xff0c;由Apache组织的开放源代码项目&#xff0c;它是接口功能、自动化、性 能测试的工具。具有高可扩展性、支持HTTP、HTTPS、TCP、等多种协议&#xff0c;还可以自己编写Java脚本对 协议进行扩展。 官…

vue 浏览器刷新报404,系统正常访问没问题

这个情况是因为vue的路由出问题了。 采用hash模式即可。 详细请了解&#xff1a; vue router 的两种路由模式hash与history的区别_vue hisotry-CSDN博客 vue默认是是hash

微信小程序(一)

WebView app.是全局配置&#xff0c;app.json是全局配置文件&#xff0c;在页面的.json配置文件中的配置会覆盖我们全局的配置 快捷键&#xff1a; .box 敲回车 ----- <view class"box"></view> .row*8 敲回车&#xff1a; .row{$}*8 敲回车 案例1&…

SpringCloud OpenFeign 服务接口调用

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第四篇&#xff0c;即介绍 Feign 和 OpenFeign 服务接口调用。 二、概述 2.1 Feign 是什么 Feign 是一…

Linux运维:深入了解 Linux 目录结构

Linux运维&#xff1a;深入了解 Linux 目录结构 一、 Linux 目录结构与 Windows之间的主要区别二、Linux根目录结构三、常见目录及其作用 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 一、 Linux 目录结构与 Windows之间的主要区别 1、根…

PXE+Kickstart无人值守安装操作系统

什么是PXE&#xff1f; PXE&#xff0c;全名Pre-boot Execution Environment&#xff0c;预启动执行环境&#xff1b; 通过网络接口启动计算机&#xff0c;不依赖本地存储设备&#xff08;如硬盘&#xff09;或本地已安装的操作系统&#xff1b; 由Intel和Systemsoft公司于199…

2007-2021年中国省级知识产权保护指数数据

2007-2021年中国省级知识产权保护指数数据 1、时间&#xff1a;2007-2021年 2、范围&#xff1a;31省市 3、指标&#xff1a;&#xff1a;年份、省份、IPP&#xff08;知识产权保护指数&#xff09; 4、来源&#xff1a;全国知识产权发展状况报告 5、指标解释&#xff1a;…

游戏党们的福利来啦~格行5G随身WiFi,王者玩家的靠谱之选!5G随身wifi品牌推荐第一名!

作为一名资深王者荣耀玩家&#xff0c;我深知网络对于游戏的重要性。那种关键时刻网络卡顿、延迟&#xff0c;导致错失战机、输掉比赛的痛苦&#xff0c;真是让人欲哭无泪。直到我的游戏搭子给我推荐了一款格行5G随身wifi&#xff0c;简直是打开新世界的大门&#xff01; 格行…

Singularity(四)| 自定义容器

Singularity&#xff08;四&#xff09;| 自定义容器 4.1 Singularity Definition 文件 对于可复制的、高质量的容器&#xff0c;我们应该使用定义文件&#xff08;Definition File&#xff09;构建 Singularity 容器 。使用定义文件的方式可以在纯文本文件中描述容器的配置和…

考研数学|汤家凤《1800》vs 张宇《1000》,怎么选?

汤家凤的1800题和张宇的1000题都是备考数学考研的热门选择&#xff0c;但究竟哪个更适合备考呢&#xff1f;下面分享一些见解。 首先&#xff0c;让我们来看看传统习题册存在的一些问题。虽然传统习题册通常会覆盖考试的各个知识点和题型&#xff0c;但其中一些问题在于它们可…

论文阅读:Editing Large Language Models: Problems, Methods, and Opportunities

Editing Large Language Models: Problems, Methods, and Opportunities 论文链接 代码链接 摘要 由于大语言模型&#xff08;LLM&#xff09;中可能存在一些过时的、不适当的和错误的信息&#xff0c;所以有必要纠正模型中的相关信息。如何高效地修改模型中的相关信息而不影…

LeetCode 每日一题 Day 95-101

2917. 找出数组中的 K-or 值 给你一个整数数组 nums 和一个整数 k 。让我们通过扩展标准的按位或来介绍 K-or 操作。在 K-or 操作中&#xff0c;如果在 nums 中&#xff0c;至少存在 k 个元素的第 i 位值为 1 &#xff0c;那么 K-or 中的第 i 位的值是 1 。 返回 nums 的 K-o…

SpringBoot扩展篇:Spring注入 @Autowired @Resource

Spring注入 Autowired & Resource 1. 概述1.1 职责1.2 流程概述 2. Demo3. AutowiredAnnotationBeanPostProcessor注册4. 注册元数据4.1 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition4.2 AutowiredAnnotationBeanPostProcessor#findAutowiringMe…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十二)-Multi-RTT定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

Hive面经

hive原理 Hive 内部表和外部表的区别Hive 有索引吗运维如何对 Hive 进行调度ORC、Parquet 等列式存储的优点数据建模用的哪些模型&#xff1f;1. 星型模型2. 雪花模型3. 星座模型 为什么要对数据仓库分层&#xff1f;使用过 Hive 解析 JSON 串吗sort by 和 order by 的区别数据…

React 教程

学习主要来源 React 教程 | 菜鸟教程 React 是一个用于构建用户界面的 JAVASCRIPT 库。 React 主要用于构建 UI&#xff0c;很多人认为 React 是 MVC 中的 V&#xff08;视图&#xff09;。 React 起源于 Facebook 的内部项目&#xff0c;用来架设 Instagram 的网站&#xff0…