Java Synchronized锁的使用详解

 更新时间:2022年10月26日 11:24:08   作者:小威要向诸佬学习呀  
在多线程并发问题中,常用Synchronized锁解决问题。本篇文章主要介绍了并发编程中Synchronized锁的用法知识记录,感兴趣的小伙伴可以了解一下

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方法也是线程安全的。

到此这篇关于Java Synchronized锁的使用详解的文章就介绍到这了,更多相关Java Synchronized锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 二分法算法的实例

    java 二分法算法的实例

    这篇文章主要介绍了java 二分法算法的实例的相关资料,希望通过本文大家能够掌握二分法,需要的朋友可以参考下
    2017-09-09
  • Java使用Poi导出Excel表格方法实例

    Java使用Poi导出Excel表格方法实例

    这篇文章主要给大家介绍了关于Java使用Poi导出Excel表格的相关资料,Java POI是一个用于操作Microsoft Office格式的Java API库,可以使用它来导出Excel文件,需要的朋友可以参考下
    2023-10-10
  • 轻松掌握Java建造者模式

    轻松掌握Java建造者模式

    这篇文章主要帮助大家轻松掌握Java建造者模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Java设计模式之观察者模式

    Java设计模式之观察者模式

    本文详细讲解了Java设计模式之观察者模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • 如何在 Spring Boot 中配置和使用 CSRF 保护

    如何在 Spring Boot 中配置和使用 CSRF 保护

    CSRF是一种网络攻击,它利用已认证用户的身份来执行未经用户同意的操作,Spring Boot 提供了内置的 CSRF 保护机制,可以帮助您防止这种类型的攻击,这篇文章主要介绍了Spring Boot 中的 CSRF 保护配置的使用方法,需要的朋友可以参考下
    2023-09-09
  • Proxy实现AOP切面编程案例

    Proxy实现AOP切面编程案例

    这篇文章主要介绍了Proxy实现AOP切面编程案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • SpringMVC使用第三方组件实现文件上传

    SpringMVC使用第三方组件实现文件上传

    这篇文章主要介绍了SpringMVC使用第三方组件实现文件上传,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Springboot 前后端分离项目使用 POI 生成并导出 Excel的操作方法

    Springboot 前后端分离项目使用 POI 生成并导出 Excel的操作方法

    在做一个 SpringBoot 前后端分离项目的时候,需要将数据存到 Excel中,用户可以下载 Excel,具体实现是采用 Apache 强大的 POI,本文给大家介绍Springboot 前后端分离项目使用 POI 生成并导出 Excel相关知识,感兴趣的朋友一起看看吧
    2023-09-09
  • Java使用锁解决银行取钱问题实例分析

    Java使用锁解决银行取钱问题实例分析

    这篇文章主要介绍了Java使用锁解决银行取钱问题,结合实例形式分析了java线程同步与锁机制相关原理及操作注意事项,需要的朋友可以参考下
    2019-08-08
  • Java设计模式中的单例模式解析

    Java设计模式中的单例模式解析

    这篇文章主要介绍了Java设计模式中的单例模式解析,单例模式确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,需要的朋友可以参考下
    2023-11-11

最新评论