「 代码性能优化 」作为一名Java程序员,你真的了解 synchronized 吗?(一)

简介: synchronized 是Java中的关键字,是一种同步锁,本文将详细介绍 Java 中 Synchronized 用法,感兴趣的小伙伴跟博主一起讨论下。

前言

synchronized 是Java中的关键字,是一种同步锁,本文将详细介绍 JavaSynchronized 用法,感兴趣的小伙伴跟博主一起讨论下。

什么是synchronized

前言中提到,Synchronized是一种同步锁,是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。

1、Java中锁机制的特性

1.1.互斥性(原子性)

所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。但是像i++、i+=1等操作字符就不是原子性的,它们是分成读取、计算、赋值几步操作,原值在这些步骤还没完成时就可能已经被赋值了,那么最后赋值写入的数据就是脏数据,无法保证原子性。被 synchronized 修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的锁,直到执行完才能释放,这中间的过程无法被中断(除了已经废弃的stop()方法),即保证了原子性。

注意!synchronized 经常会和 volatile 作比较,它们俩特性上最大的区别就在于原子性,volatile 不具备原子性。

1.2.可见性

synchronizedvolatile 都具有可见性,其中 synchronized 对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到主存当中,保证资源变量的可见性,如果某个线程占用了该锁,其他线程就必须在锁池中等待锁的释放。而 volatile 的实现类似,被 volatile 修饰的变量,每当值需要修改时都会立即更新主存,主存是共享的,所有线程可见,所以确保了其他线程读取到的变量永远是最新值,保证可见性。

1.3.有序性

有序性指程序执行的顺序按照代码先后执行。

synchronizedvolatile 都具有有序性,Java允许编译器和处理器对指令进行重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性。synchronized 保证了每个时刻都

只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。

1.4.重入性

synchronizedReentrantLock 都是可重入锁。当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属

于重入锁。通俗一点讲就是说一个线程拥有了锁仍然还可以重复申请锁。

2、Java中锁的分类

2.1. 对象锁

在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰

2.2. 类锁

在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁

如何使用synchronized

synchronized 的用法可以从两个维度上面分类:

1. 根据修饰对象分类

1.1 修饰代码块

synchronized(this|object) {}

synchronized(类.class) {}

1.2 修饰方法

修饰非静态方法

修饰静态方法

2. 根据获取的锁分类

2.1 获取对象锁

synchronized(this|object) {}

修饰非静态方法

2.2 获取类锁

synchronized(类.class) {}

修饰静态方法

synchronized的锁膨胀

锁解决了数据的安全性,但是同样带来了性能的下降,hotspot 虚拟机的作者经过调查发现,大部分情况下,加锁的代码不仅仅不存在多线程竞争,而且总是由同一个线程多次获得。所以基于这样一个概率,synchronized 在JDK1.6 之后做了一些优化,为了减少获得锁和释放锁来的性能开销,引入了偏向锁、轻量级锁,锁的状态根据竞争激烈的程度从低到高不断升级。总结来讲,锁有四种状态,并且会因实际情况进行膨胀升级,其膨胀方向是:无锁——>偏向锁——>轻量级锁——>重量级锁,并且膨胀方向不可逆。

1. 无锁

无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

2. 偏向锁

偏向锁是 JDK1.6 中引用的优化,它的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的性能。大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。偏向锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要同步。一句话总结它的作用:减少统一线程获取锁的代价

3. 轻量级锁

轻量级锁也是在 JDK1.6 中引入的新型锁机制。它不是用来替换重量级锁的,它的本意是在没有多线程竞争的情况下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。是指当锁是偏向锁,存在其它线程申请同一个锁对象时的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。也就是说这里的其它线程只是申请锁,不存在两个线程同时竞争锁,可以是一前一后地交替执行同步块。

4. 重量级锁

指的是原始的Synchronized的实现,重量级锁的特点:当同一时间有多个线程竞争锁时,都会被阻塞,只有持有锁的线程释放锁之后才会唤醒这些线程此时其申请锁带来的开销也就变大。重量级锁一般使用场景会在追求吞吐量,同步块或者同步方法执行时间较长的场景。

总结

以上内容从Java中锁的特性、分类及优化三方面介绍了Synchronized的知识,但并未触及底层原理,请继续关注博主,接下里会继续就Synchronized展开讨论。

下一篇文章主题

jvm中对象的结构模型、Synchronized具体使用实例


相关文章
|
1天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第13天】 在Java开发中,并发编程是一个复杂且重要的领域。它不仅关系到程序的线程安全性,也直接影响到系统的性能表现。本文将探讨Java并发编程的核心概念,包括线程同步机制、锁优化技术以及如何平衡线程安全和性能。通过分析具体案例,我们将提供实用的编程技巧和最佳实践,帮助开发者在确保线程安全的同时,提升应用性能。
10 1
|
2天前
|
Java
Java的取余如何编写代码
【5月更文挑战第9天】Java的取余如何编写代码
14 5
|
2天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第12天】 在现代软件开发中,多线程编程是提升应用程序性能和响应能力的关键手段之一。特别是在Java语言中,由于其内置的跨平台线程支持,开发者可以轻松地创建和管理线程。然而,随之而来的并发问题也不容小觑。本文将探讨Java并发编程的核心概念,包括线程安全策略、锁机制以及性能优化技巧。通过实例分析与性能比较,我们旨在为读者提供一套既确保线程安全又兼顾性能的编程指导。
|
3天前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
55 2
|
3天前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第11天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个方面,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。我们将通过实例和代码片段来说明这些概念和技术。
4 0
|
4天前
|
Java
代码实例演示Java字符串与输入流互转
代码实例演示Java字符串与输入流互转
|
5天前
|
存储 安全 Java
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
|
5天前
|
缓存 Java 程序员
关于创建、销毁对象⭐Java程序员需要掌握的8个编程好习惯
关于创建、销毁对象⭐Java程序员需要掌握的8个编程好习惯
关于创建、销毁对象⭐Java程序员需要掌握的8个编程好习惯
|
1月前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
255 1
|
Java BI API
在Java代码中打日志需要注意什么?
日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手,是撕逼和甩锅的利器!
329 0