文章目录

  • 前言
  • 一.synchronized的特性
  • 二.synchronized的使用
    • 2.1 同步方法
    • 2.2 同步代码块
    • 2.3 静态同步方法
    • 2.4 同步代码块和volatile关键字
  • 三.synchronized的锁机制
    • 3.1 synchronized是什么锁机制
    • 3.2 synchronized的工作过程
      • 加锁过程
    • 四. 其他的优化过程
      • 锁消除
      • 锁粗化

前言

我们先来看一下什么是synchronized,我做出了以下解释,大家可以看一下.
1.synchronized 是Java中的关键字,用于实现同步机制,确保在多线程环境下对共享资源的访问的安全性。
2.synchronized 可以被用来修饰方法或代码块,其作用是在同一时刻,只能有一个线程执行被 synchronized 修饰的代码,其他线程必须等待锁的释放才能继续执行。

3.在使用 synchronized 时,需要指定锁对象,锁对象可以是任意对象,只有持有同一把锁的线程才能访问被 synchronized 修饰的代码块或方法。

4.当一个线程进入被 synchronized 修饰的代码块或方法时,它会尝试获取锁对象的锁,如果锁没有被其他线程持有,那么该线程会获得锁并继续执行代码,当线程退出 synchronized 修饰的代码块或方法时,它会释放锁,以便其他线程可以获取锁并执行代码。如果锁被其他线程持有,则该线程将进入阻塞状态,等待获取锁。


一.synchronized的特性

我这里开始列出synchronized的特性如下:

原子性:synchronized能够保证被它修饰的代码块或方法的执行是原子性的,即在同一时刻只能有一个线程进入被synchronized修饰的代码块或方法。

可重入性:synchronized是可重入的,即在一个线程已经持有某个对象的锁时,它可以再次进入一个synchronized块或方法。

可见性:synchronized能够保证共享变量在多线程间的可见性,即一个线程修改了共享变量的值,另一个线程能够立即看到该变量的最新值。

互斥性:synchronized能够保证同一时刻只有一个线程能够进入被synchronized修饰的代码块或方法,从而保证线程之间的互斥性。

有序性:synchronized能够保证同一时刻只有一个线程能够执行被synchronized修饰的代码块或方法,从而保证线程之间的有序性。


二.synchronized的使用

我先给出一个综合的代码实例,来基础的展示一下synchronized的使用,代码如下:
这个代码我们就是用加锁操作,来保证俩个线程对count++的操作是原子性的.

public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public static void main(String[] args) throws InterruptedException {SynchronizedExample example = new SynchronizedExample();// 创建两个线程来同时增加计数器的值Thread thread1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {example.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {example.increment();}});thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();System.out.println("Count: " + example.count);}
}

2.1 同步方法

public class Counter {private int count;public synchronized void increment() {count++;}public synchronized void decrement() {count--;}public synchronized int getCount() {return count;}
}

2.2 同步代码块

public class Example {private Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}public void decrement() {synchronized (lock) {count--;}}public int getCount() {synchronized (lock) {return count;}}
}

2.3 静态同步方法

public class Example {private static int count;public static synchronized void increment() {count++;}public static synchronized void decrement() {count--;}public static synchronized int getCount() {return count;}
}

2.4 同步代码块和volatile关键字

public class Example {private volatile int count = 0;public void increment() {synchronized (this) {count++;}}public void decrement() {synchronized (this) {count--;}}public int getCount() {return count;}
}

这里我简单的介绍了synchronized的使用方法,但synchronized并不是一个单独的个体,可以根据不同场景,综合使用.


三.synchronized的锁机制

synchronized的锁机制,我先简单的阐述一下,什么是锁机制.
锁机制通过对共享资源的加锁来保证同一时刻只有一个线程可以访问该资源,其他线程必须等待锁的释放后才能访问。通过锁机制,可以有效地避免竞态条件的出现,从而保证程序的正确性和稳定性。

对于synchronized的来说,synchronized使用的是对象级别的锁机制,即每个对象都有一个与之关联的锁(也称为监视器)。在synchronized关键字加锁的代码块执行期间,该对象的锁被获取,其他线程无法访问该对象的synchronized代码块,只能等待当前线程执行完毕释放锁之后才能访问。

3.1 synchronized是什么锁机制

synchronized是Java中的一种锁机制,用于控制多个线程对共享资源的访问。synchronized可以用来修饰方法或代码块,在修饰方法时,锁住的是整个方法,而在修饰代码块时,锁住的是代码块中的对象。

synchronized锁机制的实现基于Java中的内置锁,也称为监视器锁。每个Java对象都有一个内置锁,可以用来同步访问该对象的代码。当一个线程获取了一个对象的内置锁后,其他线程必须等待该线程释放锁才能获取该对象的锁。

3.2 synchronized的工作过程

结合我们学习的锁策略,我们对synchronized进行以下总结:

  1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
  2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
  3. 实现轻量级锁的时候大概率用到的自旋锁策略
  4. 是一种不公平锁
  5. 是一种可重入锁
  6. 不是读写锁

加锁过程

这上面解释了,synchronized的加锁过程,其中自旋锁和重量级锁,我们都知道是什么,但对偏向锁,好像没有太多的概念,我这里先解释一下什么是偏向锁.
这里还会来举一个生活中的小例子.女神和男神的故事

偏向锁,只是先让线程针对锁,有个标记(做个标记很快的,非常轻量)
(偏向锁,只是先让线程针对锁,有个标记(做个标记很快的,非常轻量)

如果整个代码执行过程中,都没有遇到别的线程和我竞争这个锁,此时就不用真加锁了!!!
如果整个代码执行过程中,都没有遇到别的线程和我竞争这个锁~~此时就不用真加锁了!

但是一旦,要是有别的线程尝试来竞争这个锁!!!于是偏向锁就立即升级成真的锁(轻量级锁),此时别的线程只能等待
但是一旦,要是有别的线程尝试来竞争这个锁!于是偏向锁就立即升级成真的锁(轻量级锁),此时别的线程只能等待.
既保证了效率,又保证了线程安全!!
既保证了效率,又保证了线程安全!!

如果还没有明白,我这里再举一个实际的代码例子

public class MyObject {private int x;private int y;public synchronized void increment() {x++;y++;}
}

如果在多线程环境下,多个线程同时对 obj 进行操作,那么就可能会发生竞争条件,从而导致结果不可预测。此时,我们可以使用偏向锁来优化。

偏向锁的实现方式是,如果一个线程获取了该对象的锁,那么该对象就会被标记为偏向模式,并且该线程的 ID 会被记录在对象头中。接下来,如果该线程再次请求锁,就无需进行竞争,直接获取锁即可。这样就可以避免多线程竞争的开销。

例如,我们可以在一个单线程环境下进行如下操作:

MyObject obj = new MyObject();
obj.increment(); // 偏向锁对象
obj.increment(); // 无需竞争,直接获取锁

在第二次调用 increment() 方法时,由于只有一个线程在操作该对象,因此可以直接获取锁,无需进行竞争。这样就可以提高程序的性能

然后我们继续来说明synchronized工作过程的下一个阶段,就是进入偏向锁,之后,随着其他线程进入竞争, 偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁).
此处的轻量级锁就是通过 CAS 来实现.

通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)
如果更新成功, 则认为加锁成功
如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU).

最后,如果竞争进一步激烈, 自旋不能快速获取到锁状态, 就会膨胀为重量级锁
此处的重量级锁就是指用到内核提供的 mutex .
执行加锁操作, 先进入内核态.
在内核态判定当前锁是否已经被占用
如果该锁没有占用, 则加锁成功, 并切换回用户态.
如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.
经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒
这个线程, 尝试重新获取锁.

四. 其他的优化过程

锁消除

简单来说就是非必要不加锁
举一个简单的例子,我们经常使用的StringBuilder和StringBuffer
一般来说,我们说StirngBuffer是线程安全的,为什么呢?大家可以看一下源码

这里实际上对他们都进行了加锁,但说实话,我们在使用他们的时候,不一定都是多线程的一个环境,如果是单线程使用,就不涉及线程安全的问题了,但这个但是这个synchronized是不是也写了?,但其实在编译阶段,没有被真正的去编译,这样的操作就是锁消除,可见编译器的优化,很重要,它还是比我们聪明的.
最后来总结一下:
锁消除是指在编译期间,由于分析程序的执行情况发现一些同步操作不会出现竞争,编译器会自动消除这些不必要的同步操作,从而提高程序的执行效率。

锁粗化

画一个小小的图,大家来感受一下:


大家如果还是模模糊糊,我来一个实际的代码,大家来体验一下:
例如,下面的代码中,循环内部的加锁和解锁操作会重复执行多次


for (int i = 0; i < 1000; i++) {lock();// do somethingunlock();
}

如果将这些加锁解锁操作合并为一次加锁解锁操作,代码如下所示:

lock();
for (int i = 0; i < 1000; i++) {// do something
}
unlock();

这样做可以减少加锁解锁的次数,从而提高程序的执行效率。

最后来小小的总结一下:
锁粗化是一种优化技术,它的目的是将多个连续的加锁、解锁操作合并成一个更大的锁操作,从而减少加锁解锁的次数,减小锁的竞争。

关于synchronized的介绍相关推荐

  1. Synchronized详细介绍之锁升级过程

    Synchronized详细介绍之锁升级过程 前言 线程与进程的区别 进程 线程 区别 协程 JVM线程调度原理 JVM线程调用过程 JAVA线程与内核线程的关系 源码分析 线程状态 Synchron ...

  2. JUC学习(一):synchronized的介绍及使用(实现多线程卖票)

    一.synchronized简介 synchronized 是 Java 中的关键字,是一种同步锁.它修饰的对象有以下几种: 1.修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{} ...

  3. 乐观锁(CAS)和悲观锁(synchronized)的详细介绍

    1. 锁的定义 在代码中多个线程需要同时操作共享变量,这时需要给变量上把锁,保证变量值是线程安全的. 锁的种类非常多,比如:互斥锁.自旋锁.重入锁.读写锁.行锁.表锁等这些概念,总结下来就两种类型,乐 ...

  4. [绍棠] 关于 @synchronized

    @synchronized 结构所做的事情跟锁(lock)类似:它防止不同的线程同时执行同一段代码.但在某些情况下,相比于使用 NSLock 创建锁对象.加锁和解锁来说,@synchronized 用 ...

  5. 多线程——synchronized详解

    多线程--synchronized详解 "当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下 的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用 ...

  6. shiro如何保证session不失效_请问在不加锁的情况下如何保证线程安全?

    概念 compare and swap,解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数--内存位置(V).预期原值(A)和新值(B).如果内存位置的值与预期原值相匹配,那么 ...

  7. Java之List系列--ArrayList扩容的原理

    原文网址:Java之List系列--ArrayList扩容的原理_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Java的ArrayList是如何进行扩容的.即:扩容的机制. 重要大小 类 初 ...

  8. 《Jave并发编程的艺术》学习笔记(1-2章)

    Jave并发的艺术 并发编程的挑战 上下文切换 CPU通过时间片分配算法来循环执行任务,当前时间片执行完之后会切换到下一个任务.但是,切换会保存上一个任务的状态,一遍下次切换回这个任务时,可以再次加载 ...

  9. java 并发编程学习之二 ---- lock

    在Java中有两种方法实现锁机制,一种是在前一篇博客中([java7并发编程实战]-–线程同步机制:synchronized)介绍的synchronized,而另一种是比synchronized更加强 ...

最新文章

  1. C语言-转义字符注意事项
  2. 基于STM32F103ZET6 HC_SR04超声波测距模块
  3. go tcp连接_TCP漫谈之keepalive和time_wait
  4. python下载大文件mp4_Python3 使用requests模块显示下载大文件显示进度
  5. 美国之旅-出发前的准备
  6. python 3.9 发布_Python 3.9.0 稳定版发布
  7. baq在聊天中啥意思_职场中的“老实人”如何实现逆袭,得到领导的重用?
  8. android 编译sdk,android编译sdk
  9. 剑指offer:2.二维数组的查找(Java版)
  10. (day 53 - 动态规划 ) 剑指 Offer 63. 股票的最大利润
  11. 渗透测试-安卓APP经验总结
  12. java 16进制_JAVA 十六进制与字符串的转换
  13. 新书推荐——Linux系统管理与服务器配置
  14. python将png转为jpg,Python OpenCV读取png图像转成jpg图像存储的方法
  15. python练习54:取一个整数a从右端开始的4〜7位
  16. 自助破解winrar
  17. 栅格化渲染源码解析-neural_renderer源码(三)栅格化
  18. 猿人学web端爬虫攻防大赛赛题解析_第九题:js混淆-动态cookie2
  19. 趣点赞打不开显示服务器错误,资讯 – CSGO辅助
  20. PHP isset()与empty()的区别

热门文章

  1. win10玩cf不能全屏_如何优化Win10游戏流畅运行?Win10游戏流畅运行优化教程
  2. IE调试网页之六:使用 F12 开发人员工具调试 HTML 和 CSS (Windows)
  3. EMG推进器EB800-60II
  4. Adobe Illustrator软件安装下载
  5. 关于CCleaner 卸载程序删除所有软件文件的现象
  6. Pro ASP.NET MVC 3 Framework 译文(一)
  7. PayPal PHP接口 paypal在线支付
  8. 图解 Word2Vec
  9. 神经网络作图工具下载,神经网络和图神经网络
  10. windows mobile ?