Java中synchronized关键字到底怎么用,这个例子一定要看!

news/2024/4/30 17:37:33/文章来源:https://blog.csdn.net/Candyz7/article/details/127170277

在平时开发中,synchronized关键字经常遇到,你知道synchronized怎么用吗?本文给大家介绍一下。

我们有两种方法使用同步:

  • 使用同步方法
  • 使用同步语句或块

使用同步方法

要使方法同步,只需将synchronized关键字添加到其声明中:

public class SynchronizedDemo {private int i = 0;public synchronized void add() {i++;}public synchronized void del() {i--;}public synchronized int getValue() {return i;}
}
复制代码

如上代码显示,一共有三个同步方法:

  • add()
  • del()
  • getValue()

每个方法同一个对象同一时刻只会被调用一次,比如一个线程在调用add()时,其他线程都会被阻塞,直到第一个线程处理完add()方法。

使用同步语句或块

    public void del(int value){synchronized(this){this.i -= value;}}
复制代码

如上代码,synchronized加在了一个{}代码前,这个就代表是一个同步代码块。

以上就是synchronized关键字两种使用方法,下面我们来简单的介绍一下同步相关的概念。

什么是同步?

同步是一个控制多个线程访问任何共享资源的进程,可以避免不一致的结果。使用同步的主要目的是避免线程的不一致行为,防止线程干扰。

在java中可以使用synchronized 关键字实现同步的效果,synchronized只能应用于方法和块,不能应用于变量和类。

为啥需要同步?

首先我们来看一段代码:

public class SynchronizedDemo {int i;public void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();synchronizedDemo.increment();System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

每当调用increment()方法时计算值都会加1:

调用2次就会加2,调用3次就会加3,调用4次就会加4:

public class SynchronizedDemo {int i;public void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();synchronizedDemo.increment();synchronizedDemo.increment();synchronizedDemo.increment();synchronizedDemo.increment();System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

现在我们扩展一下上面的例子,创建一个线程去调用10次increment()方法:

public class SynchronizedDemo {int i;public void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();Thread thread = new Thread(() -> {for (int i = 1; i <= 10; i++) {synchronizedDemo.increment();}});thread.start();try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

此时计算的结果正如我们预料的那样,结果为10.

这是单线程的情况,一切都是如此的美好,但是事实真的如此吗?如果是多线程环境,会是什么样的?

下面我们来演示一下多线程的情况!

public class SynchronizedDemo {int i;public void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();Thread thread1 = new Thread(() -> {for (int i = 1; i <= 1000; i++) {synchronizedDemo.increment();}});Thread thread2 = new Thread(() -> {for (int i = 1; i <= 1000; i++) {synchronizedDemo.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

如上代码,我们创建了两个线程 thread1 和 thread2,每个线程调用1000次increment(),理论上最终打印的值应该是2000,因为thread1调用increment()1000次后值会变成1000,thread2调用increment()1000次后值会变成2000.

我们执行一下,看看结果:

结果和我们想的不一样,小于2000,我们再执行一下:

结果还是小于2000.

这是为什么呢?

因为多线程支持并行处理,因此,两个线程总是有可能同时获取计数器的值,因此都得到相同的计数器值,所以在这种情况下,不是递增计数器的值两次,只增加一次。

那么,如何避免这种情况呢?

使用 synchronized 关键字即可解决。

我们只需要将increment()方法加上synchronized就可以了:

public class SynchronizedDemo {int i;public synchronized void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();Thread thread1 = new Thread(() -> {for (int i = 1; i <= 1000; i++) {synchronizedDemo.increment();}});Thread thread2 = new Thread(() -> {for (int i = 1; i <= 1000; i++) {synchronizedDemo.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

这个时候我们再执行一下:

可以看到,值为2000.

我们把计算次数提高到10000次:

public class SynchronizedDemo {int i;public synchronized void increment() {i++;}public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();Thread thread1 = new Thread(() -> {for (int i = 1; i <= 10000; i++) {synchronizedDemo.increment();}});Thread thread2 = new Thread(() -> {for (int i = 1; i <= 10000; i++) {synchronizedDemo.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("计算值为:" + synchronizedDemo.i);}
}
复制代码

执行结果为:

可以看出,一个小小的synchronized竟然那么简单的就解决了这个问题。

这个背后的原理就是线程1执行increment()方法时,因为有synchronized,所以会自动将此方法加锁,而此时只有线程1拥有这把锁,其他线程只能等待,直到线程1释放这把锁,线程2才能参与调用。

同理,当线程2去调用increment()时,线程2拿到锁,线程1进入等待,直到线程2释放锁,就这样,直到计算完毕,在此过程中,不会出现计算错误的情况。

总结

  • synchronized 关键字是使块或方法同步的唯一方法。
  • synchronized 关键字提供了锁的特性,它确保线程之间不会出现竞争条件。被锁定后,线程只能从主存中读取数据,读取数据后,它会刷新写操作,然后才能释放锁。
  • synchronized 关键字还有助于避免程序语句的重新排序。

以上三个特性便是synchronized 关键字的精华中的精华,请大家牢记!


 

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重识Nginx - 18 网络收发与Nginx事件间的对应关系

文章目录概述网络传输TCP流与报文TCP 协议与非阻塞接口读事件写事件WireShark抓包分析WireShark设置抓包分析概述 Nginx是一个事件驱动的框架&#xff0c; 所谓事件即网络事件。 Nginx每个连接自然对应两个网络事件&#xff0c;即 读事件和写事件。 要想理解Nginx的原理&…

【牛客刷题--SQL篇】SQL9查找除复旦大学的用户信息SQL10用where过滤空值练习

个人主页&#xff1a;与自己作战 大数据领域创作者 牛客刷题系列篇&#xff1a;【SQL篇】【Python篇】【Java篇】 推荐刷题网站注册地址&#xff1a;【牛客网–SQL篇】 推荐理由&#xff1a;从0-1起步,循序渐进 网址注册地址&#xff1a;【牛客网–注册地址】 文章目录一、条件…

算法得到3次(2次、3次、4次、5次、n次)B样条曲线公式( Matlab)

Matlab得到2次B样条曲线公式 Matlab得到3次B样条曲线公式 Matlab得到4次B样条曲线公式 Matlab得到5次B样条曲线公式 Matlab得到6次B样条曲线公式 Matlab得到。。。。曲线公式 Matlab得到n次B样条曲线公式 B样条曲线公式 %% %%%%%%%%%%%%%%%%%%%%%%%B样条曲线测试%%%%%%%…

grep练习题

找出有关root的行 grep root pwd.txt找出root用户的行 grep ^root /etc/passwd匹配以root开头的行或者以dwj开头的行 grep ^(root|dwj) /etc/passwd过滤出bin开头的行&#xff0c;且显示行号 grep -n ^bin /etc/passwd过滤掉root开头的行 grep -v ^root /etc/passwd统计dwj用户…

【问题解决】大佬亲授的姿势——PlatformIO生成bin文件方法

微信关注公众号 “DLGG创客DIY”设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。最近写创客项目程序基本都用PlatformIO&#xff08;以后简称PIO&#xff09;&#xff0c;PIO在很多方面都优于arduino IDE&#xff0c;今天就不展开了啊&#xff0c;回头专门起一篇文…

Probabilistic Case-based Reasoning forOpen-World Knowledge Graph Completion

摘要 基于案例的推理(CBR)系统通过检索与给定问题相似的“案例”来解决新问题。如果这样一个系统能够达到很高的精度&#xff0c;它就会因为它的简单性、可解释性和可扩展性而具有吸引力。 在本文中&#xff0c;我们证明了这样一个系统是可实现的推理知识库(KBs)。我们的方法…

大数据讲课笔记4.2 HDFS架构和原理

文章目录零、学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;HDFS存储架构&#xff08;二&#xff09;HDFS文件读写原理1、HDFS写数据原理2、HDFS读数据原理三、归纳总结四、上机操作零、学习目标 了解HDFS存储架构理解HDFS文件读写原理 一、导入新课 通过上次…

[Linux-文件I/O] 文件函数系统文件接口缓冲区文件描述符dup2inode软硬链接动静态库

[Linux-文件I/O] 文件函数&系统文件接口&缓冲区&文件描述符&dup2&inode&软硬链接&动静态库文件IOC语言文件操作系统接口文件操作文件描述符文件描述符分配进程和文件之间的对应关系是如何建立的&#xff1f;打开用openmode关闭用close读用read写用…

TEE OS中断篇(一):系统的中断处理

前面我学习了线程方面的东西&#xff0c;这个假期&#xff0c;空闲了来看看《手机安全和可信应用开发指南》这本书的中断篇。 中断处理一个完整的系统都会存在中断&#xff0c;ARMv7架构扩展出了Monitor模式而ARMv8使用EL的方式对ARM异常运行模式进行了重新定义&#xff0c;分为…

Spring 测试运行的时候提示 Unable to find a @SpringBootConfiguration 错误

Spring 进行测试的时候提示的错误信息如下: SEVERE: Caught exception while closing extension context: org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext@c63c11ed java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you n…

Flink学习笔记(4)——Flink运行架构

目录 一、Flink运行时架构 1.1 系统架构 1.1.1 整体构成 1.1.2 作业管理器&#xff08;JobManager&#xff09; 1.1.3 任务管理器&#xff08;TaskManager&#xff09; 1.2 作业提交流程 1.2.1 高层级抽象视角 1.2.2 独立模式&#xff08;Standalone&#xff09; 1.2.…

SpringCloud 使用 Turbine 聚合监控 Hystrix 健康状态

Hystrix 的降级熔断,只是被迫的折中方案,并不是我们所期望的结果,我们还是期望系统能够永远健康运行。绝大多数情况下,一个系统有很多微服务组成,在高峰期很可能个别微服务会发生降级熔断,我们必须能够通过监控才行,这样才能快速发现并解决问题。 Hystrix 是 Netflix 的…

soc的核间通信机制-->mailbox

对于mailbox&#xff0c;这个东西其实看到了很多次&#xff0c;但是一直不知道是啥。这里大概看了一下&#xff0c;知道了为甚有这个玩意儿&#xff0c;以及这个玩意相关的有啥&#xff0c;至于具体怎么使用&#xff0c;以及详细的工作原因等着以后再说吧。 正文 目前很多芯片…

微信小程序开发实战(SM周期及WXS脚本)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

webshell 提权

在我们使用cve或者其他方式获取shell 后 python -c import pty;pty.spawn("/bin/bash") 获取一个交互式的bash shell 使用id 命令可以查看当前的用户权限 查看当前的linux 系统版本 利用kali自带的漏洞检索库检索漏洞 searchsploit privilege | grep -i linux |…

【MySQL】数据库介绍以及MySQL数据库

目录 数据库介绍 数据库概述 数据表 MySql数据库 MySql安装 登录MySQL数据库 ​​​​​​​SQLyog&#xff08;MySQL图形化开发工具&#xff09; 数据库介绍 数据库概述 什么是数据库(DB:DataBase) 数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#…

实训任务1:Linux基本操作

文章目录一、实训目的二、实训要求三、实训任务1、创建并配置三个虚拟机2、创建SSH连接3、实现IP地址与主机名的映射4、关闭和禁用防火墙5、创建目录结构6、压缩打包7、安装软件包8、创建脚本文件9、直接运行脚本10、虚拟机相互免密登录11、远程拷贝文件一、实训目的 通过实训…

代谢组学和宏基因组学研究不同添加剂对青贮品质的影响

​ 发表期刊&#xff1a;Bioresource Technology 影响因子&#xff1a;9.642 百趣生物提供服务&#xff1a;代谢组学宏基因组 研究背景 人口增长促进了全球肉类和牛奶消费量增加&#xff0c;养殖所需饲料用量也逐年上升&#xff0c;发酵后的饲料是进行农副产品处理更好的选…

大数据技术Spark3.0详解

一、Spark3.0 简介 Spark3.0版本包含了3400多个补丁程序&#xff0c;是开源社区做出巨大贡献的最高峰&#xff0c;带来了Python和SQL功能的重大进步&#xff0c;并着眼于探索和生产的易用性。 1、Spark3.0新功能 &#xff08;1&#xff09;通过自适应查询执行&#xff0c;动…

基于物联网的智能厨房安全监测系统-上位机程序

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 博客写作背景----项目中解决的问题 最近遇到一个基于TCP/IP网络的远程智能物联网系统&#xff0c;采用Arduino Uno控制器作为下位机&#xff0c;采用LabVIEW作为远程监控软件&#xff0c;两者通过网络实现通信。初步定为使…