并发编程线程安全之同步锁Synchronized

2024-02-20 15:04

本文主要是介绍并发编程线程安全之同步锁Synchronized,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、原子性定义

原子性的本质是互斥访问,同一时刻只有一个线程对它进行访问操作

二、原子性问题的简述

public class AutomicDemo {int count = 0;public static void main(String[] args) throws InterruptedException {AutomicDemo automicDemo = new AutomicDemo();Thread thread1 = new Thread(() ->{for (int j = 0; j < 1000; j++) {automicDemo.incr();}});Thread thread2 = new Thread(() ->{for (int j = 0; j < 1000; j++) {automicDemo.incr();}});thread1.start();thread2.start();// join方法保证线程执行完毕thread1.join();thread2.join();System.out.println("i的结果值为:"+automicDemo.count);}public void incr(){count++;}
}

运行结果:

线程1线程2各循环一千次执行i++操作,正常情况下应该得到的值是2000,那么为什么会得到1412呢?这是由于当前代码中的i++操作是非原子性的。

其实一个count++操作是分为3步的:1.加载2.计算3.写入内存。由上图可以看出来,在同一时刻,线程A与线程B同时运行,当线程A刚把count为0的值加载到寄存器的时候,此时线程进行了切换,线程B完成了整个count++操作并把结果写入了内存,此时线程A接着执行,那么线程A加载到寄存器的count还是0,因此它计算后也把count=1存入了内存,这无形中就少了一次计算。思考:那么应该怎么解决这个问题呢?加同步锁synchronized关键字,保证在同一时刻,只有一个线程能够访问并操作count++       

思考:   为什么加了synchronized关键字 系统就会认定它为一个同步锁呢,从而避免多线程对该方法 的一个操作呢?(见3.3)synchronized的作用范围是什么呢?(见3.1)锁的本质又是什么呢?(见3.2)

三、Synchronized关键字

3.1 作用范围

  • 修饰实例方法  创建不同的对象,都可以访问该实例
  • 修饰静态方法   全局锁
  • 修饰代码块

synchronized() 括号中可以存储任何一个对象, 影响锁的作用范围,其实就是括号中对象的生命周期

3.2 抢占锁的本质

抢占锁的本质就是如何实现互斥,那么必定有两个条件

  • 共享资源
  • 锁标记  可以假设 0代表无锁  1 代表有锁

也就是说,抢占锁,一定是共同要访问一个共享的资源,当一个线程先占有了这个资源,就变为有锁状态,阻塞其它的线程。

3.3 锁信息的存储

加了synchronized关键字,系统为何就能识别这是一个同步锁呢,其实,系统根据该关键字,保存了一些信息在作用的对象上。

MarkWord对象头

我们可以通过以下代码打印对象头的信息(不加synchronized和加synchronized的区别)

   public static void main(String[] args) {Object lock = new Object();System.out.println(VM.current().details());System.out.println(ClassLayout.parseInstance(lock).toPrintable());}

打印的信息如下

结合下图可以看出,第一行的前8个字节的最后3位001 则为无锁状态

四、Synchronized锁升级

1. 无锁状态

2. 偏向锁: 假设没有线程竞争的时候,A线程进入到同步代码 就会偏向A线程 有线程竞争的时候 线程B再进来 就会做升级 轻量级锁

3. 轻量级锁   主要作用是避免线程阻塞   采用自旋锁的方式

4 .重量级锁   表示的是用户态到内核态的交换  没有获得锁的线程会阻塞,再被唤醒

思考: 线程A已经抢占到了锁,当线程B来竞争的时候,如果是重量级锁,则线程A执行完还需要唤醒线程B, 比较消耗性能,那么有没有一种办法可以避免或者优化这种阻塞呢?于是就引入了轻量级锁,线程B会不断的去重试,如果此时正好线程A结束了,那么线程B就可以执行了,不需要再去唤醒了。举个例子,就比如你去找老王,会先敲几下门,然后如果门没开,则会再去一边等着,等着老王来唤醒。

分析: 当有两个线程的时候,线程A先进入了,则线程B抢占锁的时候,则会先进行自旋,如果抢占到了,则修改lock flag的标记,使用CAS机制保证操作的原子性

五、CAS机制

old: ThreadA

expect: ThreadB

update: ThreadC

CAS机制与乐观锁类似

CAS机制其底层是C++代码,采用了lock指令

举例:

当前线程A获得了偏向锁,线程B来抢占偏向锁

线程B就会来调用CAS,把偏向锁的指针指向自己

CAS(object,线程A的指针,线程B的指针(带更新的值))

这篇关于并发编程线程安全之同步锁Synchronized的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/728604

相关文章

线程池ThreadPoolExecutor应用过程

《线程池ThreadPoolExecutor应用过程》:本文主要介绍如何使用ThreadPoolExecutor创建线程池,包括其构造方法、常用方法、参数校验以及如何选择合适的拒绝策略,文章还讨论... 目录ThreadPoolExecutor构造说明及常用方法为什么强制要求使用ThreadPoolExec

Java线程池核心参数原理及使用指南

《Java线程池核心参数原理及使用指南》本文详细介绍了Java线程池的基本概念、核心类、核心参数、工作原理、常见类型以及最佳实践,通过理解每个参数的含义和工作原理,可以更好地配置线程池,提高系统性能,... 目录一、线程池概述1.1 什么是线程池1.2 线程池的优势二、线程池核心类三、ThreadPoolE

input的accept属性让文件上传安全高效

《input的accept属性让文件上传安全高效》文章介绍了HTML的input文件上传`accept`属性在文件上传校验中的重要性和优势,通过使用`accept`属性,可以减少前端JavaScrip... 目录前言那个悄悄毁掉你上传体验的“常见写法”改变一切的 html 小特性:accept真正的魔法:让

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程

python协程实现高并发的技术详解

《python协程实现高并发的技术详解》协程是实现高并发的一种非常高效的方式,特别适合处理大量I/O操作的场景,本文我们将简单介绍python协程实现高并发的相关方法,需要的小伙伴可以了解下... 目录核心概念与简单示例高并发实践:网络请求协程如何实现高并发:核心技术协作式多任务与事件循环非阻塞I/O与连接

Redis的安全机制详细介绍及配置方法

《Redis的安全机制详细介绍及配置方法》本文介绍Redis安全机制的配置方法,包括绑定IP地址、设置密码、保护模式、禁用危险命令、防火墙限制、TLS加密、客户端连接限制、最大内存使用和日志审计等,通... 目录1. 绑定 IP 地址2. 设置密码3. 保护模式4. 禁用危险命令5. 通过防火墙限制访问6.

深入理解Redis线程模型的原理及使用

《深入理解Redis线程模型的原理及使用》Redis的线程模型整体还是多线程的,只是后台执行指令的核心线程是单线程的,整个线程模型可以理解为还是以单线程为主,基于这种单线程为主的线程模型,不同客户端的... 目录1 Redis是单线程www.chinasem.cn还是多线程2 Redis如何保证指令原子性2.

C++实现一个简易线程池的使用小结

《C++实现一个简易线程池的使用小结》在现代软件开发中,多线程编程已经成为提升程序性能的常见手段,本文主要介绍了C++实现一个简易线程池的使用小结,感兴趣的可以了解一下... 在现代软件开发中,多线程编程已经成为提升程序性能的常见手段。无论是处理大量 I/O 请求的服务器,还是进行 CPU 密集型计算的应用

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关