并发编程系列(二):线程锁使用场景和原理

C# 多线程编程:线程锁与无锁并发 多线程编程在现代软件开发中至关重要。本文将讨论 C# 中的多线程技术,重点介绍锁的概念,线程锁与无锁并发。通过学习本篇博文,我们将学会如何正确处理并发问题,提高程序的性能和稳定性。锁是一种同步机制,用于控制多个线程对共享资源的访问。当一个线程获得了锁时,其他线程将被阻塞,直到该线程释放了锁。本文深入探讨了 C# 中的多线程编程技术,重点介绍了锁的基本概念、线程锁的类型、锁的实现方式、无锁并发编程以及 C# 中的并发集合类和经典并发同步问题。理解多线程编程的基本概念。 阅读详情

问题:java中如何解决线程并行导致的数据安全问题?
java SE 1.6后对synchronized做了优化,增加了偏向锁、轻量级锁概念,以减少获得/释放锁带来的性能消耗

一、synchronized的使用
锁的粒度,从上到下变小
1.修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

public synchronized static void demo2() {
    // 代码块
}

2.修饰实例方法,作用于当前实例对象加锁,进入同步代码前要获得当前实例对象的锁

public synchronized void demo2() {
    // 代码块
}

3.修饰代码块,指定加锁对象,进入同步代码前要获得给定对象的锁

public void demo(){
    synchronized (this){ 
        // 代码块
    }
}

二、锁是如何存储的

  • 问题:锁如何实现线程的互斥特性?
  • 思路:synchronized(lock)为切入点,锁是否与对象生命周期有关,锁在对象中存在状态是怎样的

1.对象在内存中存储

对象头(header),实例数据(instance data),对齐填充(padding)
eg.

2.jvm源码实现
new 实例对象时,(Hotspot虚拟机)JVM会创建instanceOopDesc对象。
OOP-klass模型描述对象实例,OOP(Ordinary ObjectPoint)普通对象指针,klass描述对象具体类型。Hotspot采用instanceOopDesc和arrayOopDesc类型描述对象头

instanceOopDesc,继承自OopDesc类。OopDesc,定义在oop.hpp,OopDesc对象包含两个成员_mark和metadata。
_mark,表示对象标记,属于markOop类型,也即Mark Word,它记录了对象和锁有关的信息
_metadata,表示类元信息,类元信息存储的是对象指向它的类元数据(Klass)的首地址,其中Klass表示普通指针,_compressed_klass表示压缩类指针

MarkWord
markOop,定义在markOop.hpp文件,代码如下


Mark Word记录了对象和锁有关的信息,里面存储的信息会随着锁标志位的变化而变化。
Mark Word可能存在5种变化情况,以下演示32位机的(也有64位机的,存储的大小不一样)

eg.记录5种状态图表

锁状态

25bit

4bit

1bit

2bit

23bit

2bit

是否偏向锁

锁标志位

无锁

对象的HashCode

分代年龄

0

01

偏向锁

线程ID

epoch

分代年龄

1

01

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向重量级锁的指针

10

GC标记

空

11

3.为什么任何对象都可以实现锁
任何一个对象都继承Object对象,object在jvm中都有native的C++对象 oop/oopDesc对应。
线程在获取锁时,其实是获取监视器对象monitor,多线程访问同步代码块时,相当于去争抢对象监视器,以修改对象的锁标识。

三、锁升级
无锁,偏向锁,轻量级锁,重量级锁
1.偏向锁
当只有一个线程访问时,会将MarkWorld锁标志位设为“01”,并通过CAS操作把线程ID记录到MarkWord。当下次访问时,无需再次加锁和释放锁,会判断对象头里是否有当前线程ID,若有,则无需尝试获得锁(也即是锁会偏向于某个线程)
eg.偏向锁流程

2.轻量级锁
当一个线程Thread-1已经持有偏向锁,还没走完同步代码,此时若有竞争线程Thread-2尝试进入,则会将持有偏向锁线程先暂停,然后升级为轻量级锁。
竞争线程Thread-2通过自旋方式,有限次数内获得锁,如果自旋获取锁失败,则当前线程升级为重量级锁,竞争线程挂起阻塞

轻量级锁升级细节

在线程栈帧中创建锁记录,将MarkWord中标记的对象信息复制到锁记录中,将锁记录的Owner指向锁对象,将MarkWord替换为指向锁记录的指针

轻量级锁的解锁

轻量级锁的释放,获得锁的逆向逻辑。通过CAS操作将线程栈帧中的lockRecord替换回MarkWord中,若成功表示没有竞争;若失败,则存在竞争,轻量级锁会膨胀为重量级锁

eg.轻量级锁流程

3.重量级锁
若两个线程正常交替执行,那么轻量级锁基本能够满足锁的要求,若当两个线程同时进入临界区,则轻量级锁会膨胀为重量级锁,未获得锁的线程挂起阻塞

通过命令:javap -v 类.class,查看生成的class文件信息

加了同步的代码会看到monitorenter和monitorexit.
每个java对象都会与一个监视器monitor关联,要想取得synchronized修饰的同步方法或代码块的执行权限,首先需要取得修饰对象的monitor。
monitorenter表示去获得一个对象监视器,monitorexit表示释放监视器所有权,使其他被阻塞线程可以尝试获得监视器。

eg.重量级锁加锁流程


任意对Object访问的线程,首先要获得Object的监视器

四、wait,notify,notifyAll

  • 问题:通过synchronized,阻塞线程只有等到获得锁线程执行完毕才唤醒,如何显示控制线程唤醒?
  • 通过信号机制:Object提供的wait,notify,notifyAll控制线程状态。

基本概念
wait,持有对象锁线程A,准备释放对象锁权限,释放CPU资源进入等待状态。
notify,当持有对象锁线程A准备释放锁权限时,通知jvm唤醒某个竞争该锁的线程,当A执行完毕并释放锁,线程X直接获得对象锁权限,其他线程继续等待。
notifyAll,唤醒所有竞争统一对象锁的线程。

五、练习

1.请分析以下程序的执行结果,并详细说明原因

public class SynchronizedDemo implements Runnable{
    int x = 100;

    public synchronized void m1() {
        x = 1000;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("x=" + x);
    }

    public synchronized void m2() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        x = 2000;
    }
    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo sd = new SynchronizedDemo();
        new Thread(()->sd.m1()).start();
        new Thread(()->sd.m2()).start();
        sd.m2();
        System.out.println("Main x=" + sd.x);
    }
    @Override
    public void run() {
        m1();
    }
}

分析:

SynchronizedDemo sd = new SynchronizedDemo();
        new Thread(()->sd.m1()).start(); // x=1000,休眠1秒
        new Thread(()->sd.m2()).start(); // 休眠0.2秒,x=2000
        sd.m2(); // 休眠0.2秒,x=2000
        System.out.println("Main x=" + sd.x);

Main,t1(执行m1方法),t2(执行m2方法)

Main线程获得锁,x=2000
    打印时,若t1获得锁,并先于打印给x=1000,则打印Main x=1000,休眠1秒打印x=1000
    打印时,若t1获得锁,晚于打印或者t2获得锁,则输出Main x=2000,再输出x=1000

t1获得锁,打印x=1000,再打印Main x=2000(不管main还是t2获得锁,结果一样)
t2获得锁,x赋值2000
    Main获得锁后,在打印时,若t1先于打印把x赋值1000,则输出Main=1000,x=1000
    t1获得锁,x=1000,再打印Main x=2000

2.下面这个程序的最终结果是多少?为什么?

public class SynchronizedDemo  {
   static Integer count=0;
   public static void incr(){
       synchronized (count) {
           try {
               Thread.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           count++;
       }
   }
    public static void main(String[] args) throws IOException, InterruptedException {
        for(int i=0;i<1000;i++){
            new Thread(()->SynchronizedDemo.incr()).start();
        }
        Thread.sleep(5000);
        System.out.println("result:"+count);
    }
}

分析:Integer可变,所以基本可以视为没有加锁,也就是存在并发问题,值会小于1000

4种常用Java线程锁的特点,性能比较、使用场景.pdf 4种常用Java线程锁的特点,性能比较、使用场景 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。 立即下载

相关推荐

【Code】4种常用Java线程锁的特点,性能比较、使用场景

本文是对相关主题文章的代码功能展示,主要通过代码形式来验证和演示功能,以加深对知识点的理解。如有遗漏或理解不正确的地方,欢迎大家拍砖!

爱码少年 00fly.online 的博客 2万+

Ferry工单平台部署

ferry工单平台部署

运维求生之路 1897

多线程线程锁的概念和介绍

在多线程环境下,为了让多线程安全地访问和使用共享变量,必须引入锁机制。锁机制即当一个线程持有锁后,其他线程只能进行等待,直到持有锁的线程释放锁,再次重新竞争锁。

Jay的小提琴的博客 2110

MATLAB实战:手把手教你搭建构网逆变器同步稳定性仿真模型(附避坑指南)

本文详细介绍了如何在MATLAB/Simulink平台上搭建构网逆变器同步稳定性仿真模型,涵盖从基础模块配置到虚拟同步机控制实现的完整流程。针对电力电子工程师常见的控制参数整定困难、仿真结果偏差等问题,提供了LCL滤波器谐振抑制、虚拟惯量优化等关键技术解决方案,并附有避坑指南和工程实践技巧,助力研究者高效完成仿真建模。

weixin_29051193的博客 297

嵌入式常见英文2500词总结

嵌入式常见英文总结

qq_28576837的博客 1万+

线程中的锁

是指分布在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。是指分步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。信号量(同步与互斥)

hello_liuniuniu的博客 821

提交 ieee 手稿出现:Do not include the Reviewer PDF with my submission 怎么办

IEEE投稿Reviewer PDF选项说明 投稿时出现的"Do not include the Reviewer PDF with my submission"选项控制是否附带评审专用PDF文件。Reviewer PDF是系统自动生成包含所有投稿材料的完整PDF,用于同行评审。建议保持默认不勾选状态,除非确认编辑部不需要该文件,否则可能影响审稿流程。勾选表示不附带此PDF,不勾选则会正常生成并提交该评审文件。

ZhangJiqun&191cm&85kg 2206

线程锁的使用场景

什么是线程锁 两个或以上线程在访问共享资源时,为了确保这个共享资源被每个线程访问结果正确,这时就需要用到线程锁。 2 线程锁使用场景 a)多个窗口同时售票问题 b)公交车前后门上车抢坐问题 3 使用线程锁,解决多个窗口同时售票问题 public class TicketTask implements Runnable{ private int ticket ...

xiaoyaGrace的博客 1002

STM32G0/G4电机控制中三角函数与浮点运算优化实战

三角函数(sin/cos)和浮点运算是电机矢量控制(FOC)的核心基础,直接影响Clark/Park变换、SVPWM及观测器的实时性与精度。在资源受限的MCU上,标准math.h库因无硬件FPU支持而引发严重时序抖动,导致电流环延迟、转矩脉动增大甚至系统复位。通过硬件加速(如STM32G4的CORDIC协处理器)或定制化定点算法(如STM32G0的查表+线性插值),可将sin/cos计算从数十微秒压缩至1–3 μs量级,提升30倍以上效率。该方案广泛适用于BLDC/PMSM驱动、伺服控制及高动态响应嵌入式运

weixin_35751412的博客 109

Django框架中settings文件的部署建议

为了更好地管理开发和生产环境的配置,建议将settings文件分为多个文件,分别用于不同的环境。然后,通过在启动应用程序时指定不同的settings文件,来加载相应的配置。在Django框架中,settings.py文件是非常重要的配置文件,它包含了项目的各种设置和选项。在Django框架中,settings.py文件是非常重要的配置文件,它包含了项目的各种设置和选项。可以使用Python的。为了更好地管理开发和生产环境的配置,建议将settings文件分为多个文件,分别用于不同的环境。

WELL_CODER的博客 129

别再用错锁!Java 锁机制的场景化应用全解析

Java 中的锁机制丰富多样,每种锁都有其独特的特性和适用场景。在实际开发中,需要根据具体的业务需求、并发量以及性能要求,合理选择合适的锁。对于简单的同步需求,Synchronized 是便捷的选择;对于复杂逻辑和特定功能需求,ReentrantLock提供了更强大的功能;在读写操作比例差异较大的场景下,读写锁能显著提升性能;而自旋锁则适用于锁占用时间短的高并发场景。通过正确选择和使用锁机制,能够构建出高效、安全的多线程 Java 应用程序。

z1ztai的博客 970

ASProxy64.dll导致jetbrains家的IDE都无法打开。

摘要:在Windows11系统中,JetBrains IDE无法启动,报错指向ASProxy64.dll文件。该第三方DLL被注入到JVM原生层,导致内存访问冲突(错误代码0xc0000005)。经排查发现此文件来自Astrill软件。解决方案为卸载Astrill后重启电脑,问题得以解决。该案例表明第三方DLL注入可能影响IDE正常运行,卸载相关软件是最直接的解决方法。(149字)

猿小白的博客 936

java的线程锁

竞态条件是指多个线程在并发执行时,由于执行顺序的不确定性,导致程序行为不可预测。线程锁可以通过确保同一时刻只有一个线程访问共享资源,来避免这种情况。通过合理设计锁的获取和释放顺序,可以避免死锁的发生。当一个线程获取锁时,其他线程必须等待,直到该线程释放锁。若两个线程同时执行,它们可能会发生死锁,因为每个线程都在等待对方释放自己所需要的锁。线程锁是多线程编程中的一个关键概念,通过合理使用锁机制,可以提高程序的稳定性和可靠性。:最基础的锁机制,用于方法或代码块,保证同一时刻只有一个线程访问共享资源。

2303_80059852的博客 2462

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

高并发:线程、线程锁与线程池(精华)

前文: 单线程——多线程的开启——线程锁——线程同步工具——手写连接池——连接池工具类。 一、线程 1.线程的概念 2.线程与进程的关系 3.定义: 区别:如上!!! 4.wait()和sleep() 5.线程的状态及其他API 二、线程锁 线程并发同步时,引入了锁机制。 1. 普通锁机制:synchronized修饰代码块与...

weixin_42687074的博客 3896

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

线程与线程锁

线程和线程锁是并发编程中的核心概念。理解线程的工作原理以及如何使用线程锁来管理并发访问共享资源,对于编写高效、安全的并发程序至关重要。以下是线程与线程锁的机制原理与使用的详细说明。掌握线程与线程锁的机制原理与使用,可以帮助你编写高效、安全的并发程序。

blue_msboy的博客 1000

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

python 线程锁

线程锁是一种同步原语,用于协调多个线程对共享资源的访问。它的主要目的是防止多个线程同时修改同一资源,从而避免数据竞争和不一致性问题。当一个线程需要访问共享资源时,它首先尝试获取锁。如果锁是可用的,该线程获得锁,并可以安全地访问共享资源。如果锁已被其他线程持有,当前线程将被阻塞,直到锁被释放。线程完成对共享资源的访问后,必须释放锁,以允许其他线程访问该资源。通过这种机制,线程锁确保在任何给定时刻,只有一个线程可以访问受保护的共享资源,从而维护了数据的一致性和完整性。

cliffordl的专栏 1435

高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景

转载自:高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景 多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升。虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求。 使用多线程的理由之一是和进程相比,它是一种非常花销小,切换...

chengyu5033的博客 115

unity C#什么是线程锁,以及使用案例

线程锁在硬件层面通常配合内存屏障(Memory Barrier)使用,确保缓存中的数据在锁的获取和释放时与主内存保持一致,即修改后的数据会被及时刷新到主内存,而其他线程在获取锁后能从主内存重新加载最新的数据。当一个线程进入临界区时,它会先获取锁,此时其他试图获取该锁的线程将被阻塞或挂起,直到持有锁的线程执行完临界区内的代码,并释放锁。为了避免死锁的发生,线程锁的使用需要遵循一定的规则,如避免循环等待锁的情况,以及在持有锁的情况下尽量不要尝试获取其他锁(除非满足特定的锁顺序)。这样的互斥量来实现线程锁。

qqrrjj2011的博客 1597

快速理解线程锁

线程锁 线程锁真的好麻烦啊!!! 找了几篇博客发现写的都不一样 相关联内容太多不容易理解 所以现在需要理清 什么是线程锁 应用场景 怎么用 优缺点 1. 什么是线程锁机制 多线程可以同时运行多个任务 但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误! so,不使用线程锁, 可能导致错误 啰嗦两句: 比如你在银行取钱的同时你女朋友用支付宝取钱 ...

宅神的博客 2万+

线程锁及线程锁的作用

什么是线程锁机制   多线程可以同时运行多个任务但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误!   so,不使用线程锁, 可能导致错误 分布式锁,进程锁,线程锁到底是什么 在分布式集群系统的开发中,线程锁往往并不能支持全部场景的使用,必须引入新的技术方案分布式锁。   线程锁:大家都不陌生,主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至...

weixin_30421809的博客 2100

深入解析Java线程锁机制:从原理到最佳实践

在多线程编程中,线程安全是开发者面临的核心挑战之一。当多个线程并发访问共享资源时,如果没有正确的同步机制,就可能出现数据不一致、竞态条件等问题。Java提供了多种线程锁机制来保障线程安全,本文将深入探讨Java中的各种锁实现、使用场景和优化策略。

keshi12354的博客 1009

线程锁的原理是什么

线程锁的使用 本文内容何时该使用线程锁. 线程锁的写法. 以线程锁的例子来理解线程的调度。使用线程锁的场合程序中经常采用多线程处理,这可以充分利用系统资源,缩短程序响应时间,改善用户体验;如果程序中只使用单线程,那么程序的速度和响应无疑会大打折扣。 但是,程序采用了多线程后,你就必须认真考虑线程调度的问题,如果调度不当,要么造成程序出错,要么造成荒谬的结果。一个讽刺僵化体制的笑话前苏联某官员

qew110123的专栏 2478

java 并发_最全Java并发编程技能:多线程+线程池+线程锁+并发工具+并发容器

Java并发编程的技能基本涵括以下5方面:多线程线程池线程锁并发工具类并发容器多线程的4种创建方式继承Thread实现Runnable接口实现Callable接口以及线程池来创建线程Java线程的生命周期线程的创建线程的就绪线程的运行线程的阻塞线程的死亡详细请查看:Java并发编程系列:线程的五大状态,以及线程之间的通信与协作Java线程的通信与协作、1.线程就绪:调用start(),以及slee...

weixin_39854730的博客 112

Python线程锁:确保多线程程序的数据安全

Python的线程锁是一个强大的工具,用于在多线程环境中管理共享资源和。

IT策士的博客 1265
上一篇: 并发编程系列(一):线程基础
下一篇: 并发编程系列(三):重排序和可见性问题
OpenWorld1
博客等级 码龄12年 1粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值