Java Synchronized锁的使用详解
作者:小威要向诸佬学习呀 发布时间:2022-10-11 12:59:33
Synchronized的用法
在多线程并发问题中,常用Synchronized锁解决问题。Synchronized锁通常用于同步示例方法,同步静态方法,同步代码块等。
同步示例方法
我们可能自己使用过在方法前加Synchronized锁修饰,在多线程并发同时调用同一个实例化对象时,如果这个方法加上了Synchronized锁,那么也是线程安全的。
举个栗子:
package Thread;
import java.util.stream.IntStream;
public class ThreadTest {
private Long count=0L;
public void incrementCount(){
count++;
}
public Long execute() throws InterruptedException {
Thread thread1=new Thread(()->{
IntStream.range(0,1000).forEach((i)->incrementCount());//线程1循环1000次
});
Thread thread2=new Thread(()->{
IntStream.range(0,1000).forEach((i)->incrementCount());//线程2循环1000次
});
thread1.start();//开启线程
thread2.start();
thread1.join();//等待线程1和线程2执行完毕
thread2.join();
return count;
}
public static void main(String[] args) throws InterruptedException {
ThreadTest threadTest=new ThreadTest();
Long count = threadTest.execute();
System.out.println(count);
}
}
在上面的程序中,count变量为成员变量,在多线程同时使用时极大可能会发生错误,在前面也讲到过count++包含三个步骤:1.将变量count从主内存中加载到CPU的寄存器中;2.在CPU的寄存器中执行count++或++count的操作;3.将运算的count++的结果写入缓存或内存中。两个线程都会更新count的值到内存中,当其中一个线程再从内存中读取数据时,可能读到的成员变量会与当前的变量不一致,从而使得最终count的结果不为2000,因此会发生错误。
如何能解决这种错误?就是为incrementCount方法加锁:
public synchronized void incrementCount(){
count++;
}
这样就能保证所得到的count最终值为2000了。
同步静态方法
当一个类的某个静态方法加了synchronized锁时,就相当于给这个类的class对象加锁。所以无论创建多少个当前类的对象调用这个被synchronized锁修饰的静态方法时,都是线程安全的。
如上面的例子,修改如下:
package Thread;
import java.util.stream.IntStream;
public class ThreadTest {
private static Long count=0L;
public static synchronized void incrementCount(){
count++;
}
public static Long execute() throws InterruptedException {
Thread thread1=new Thread(()->{
IntStream.range(0,1000).forEach((i)->incrementCount());
});
Thread thread2=new Thread(()->{
IntStream.range(0,1000).forEach((i)->incrementCount());
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
return count;
}
public static void main(String[] args) throws InterruptedException {
ThreadTest threadTest=new ThreadTest();
Long count = threadTest.execute();
System.out.println(count);
}
}
因此,当多个线程并发执行调用被synchronized锁修饰的静态方法时,这个静态方法是线程安全的。
同步代码块
前面提到加了synchronized锁的方法在多线程并发条件下是线程安全的,但是在执行业务逻辑过多的代码块时,可能会影响程序的执行效率。对于此时,可以把一个方法分成多个小的临界区。
举个栗子:
private Long count1=0L;
private Long count2=0L;
public synchronized void incrementCount(){
count1++;
count2++;
}
在上面的代码中,count1和count2为两个不同的自增操作,因此对于count1和count2来说是两个不同的临界区资源。当一个线程进入incrementCount方法中时,会对整个方法进行加锁,在对count1进行自增操作时,也会占用count2的资源,相当于占用全部的资源。只有等到这个线程执行完count1++和count2++的操作时,释放锁时,其它线程才能拿到锁资源进入incrementCount方法。
但是这样会影响程序的性能。因为count1++和count2++为两个互不影响的两个临界区资源,当线程拿到锁,会占用两个资源,使得临界区资源进行闲置等待,因此可以优化代码,让synchronized锁修饰代码块。
修改后的代码:
private Long count1=0L;
private Long count2=0L;
public Object count1Lock=new Object();
public Object count2Lock=new Object();
public void incrementCount(){
synchronized (count1Lock){
count1++;
}
synchronized (count2Lock){
count2++;
}
}
上面代码中,对count1和count2分别建立了对象锁count1Lock和count2Lock,而没有对incrementCount加锁,意为当一个线程进入incrementCount方法时,其他线程也能进入此方法,当线程1拿到count1Lock对象锁时,不影响线程2拿到count2Lock对象锁来对count2执行自增操作。
这样既提高了程序的执行效率,同时,由于临界区资源都加了锁,incrementCount方法也是线程安全的。
来源:https://blog.csdn.net/qq_53847859/article/details/127520451


猜你喜欢
- 一、事务的基本原理Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操
- 什么是JPA一种规范,并非ORM框架,也就是ORM上统一的规范spring-boot-starter-data-jpa 
- Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入
- 今天练习C#的一个功能,就是将一个字符串时行翻转显示如:string str = "Insus.NET";翻转成为:st
- 本文实例讲述了C#中DataGridView常用操作。分享给大家供大家参考。具体如下:public void Binder1(){ Data
- 应用特性:在很多复杂而小功能需要调用需求时,而且这些调用往往还有一定相关性,即一调用就是一系列的。结构特性:把原本复杂而繁多的调用,规划统一
- 很多时候在进行C#项目的实际开发中,会需要根据条件来设置节点不可勾选,查看DevExpress文档发现通过其CustomDrawNodeCh
- 现如今打开一个 App,比如头条、微博,都会有长列表,随着我们不断地滑动,视窗内的内容也会不断地更新。今天就用 Flutter 实现一下这种
- 本章节更加具体化的学习编译器还有哪些可以优化的方便,让你的应用展现出更好的性能。JIT编译器版本JIT编译器有不同的版本,而最终你使用哪种,
- 使用filter对request body参数进行校验@Slf4jpublic class ParameterCheckServletReq
- 前言RabbitMQ 是使用 Erlang 语言开发的消息中间件, 其遵循了高级消息队列协议(Advanced Message Queuin
- 排序算法介绍排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。排序的分类:1) 内部排序:指将
- 本文实例讲述了C#创建windows系统用户的方法。分享给大家供大家参考。具体如下:下面的代码可以通过c#创建一个windows的本地系统账
- 本文实例讲述了Java实现分解任意输入数的质因数算法。分享给大家供大家参考,具体如下:分解任意输入数的质因数:质因数概念:任何一个合数都可以
- 目录例1: 以下代码输出什么?例2: 为什么虚函数效率低?虚继承例3: 请评价多重继承的优点和缺陷。例4: 在多继承的时候,如果一个类继承同
- 通常,在这个页面中会用到很多控件,控件会用到很多的资源。Android系统本身有很多的资源,包括各种各样的字符串、图片、动画、样式和布局等等
- 本文实例讲述了Android开发实现判断通知栏是否打开及前往设置页面的方法。分享给大家供大家参考,具体如下:项目中用到日程提醒功能,如果应用
- 匿名内部类:先举个例子吧,给大家看一下什么是匿名内部类,Endeavor刚刚接触的时候,觉得哇哦,好奇怪的样子,这也太别扭了吧,不知道大家是
- 本文实例讲述了Android编程之动态壁纸。分享给大家供大家参考,具体如下:从android 2.1版本起引入了动态壁纸的概念,熟悉andr
- ViewDragHelper是support.v4下提供的用于处理拖拽滑动的辅助类,查看Android的DrawerLayout源码,可以发