浅析java并发中的Synchronized关键词

 更新时间:2020年02月26日 10:06:43   作者:by flydean  
这篇文章主要介绍了java并发中的Synchronized关键词,本文通过思路代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。

java提供了synchronized关键字,方便我们实现上述操作。

为什么要同步

我们举个例子,我们创建一个类,提供了一个setSum的方法:

public class SynchronizedMethods {

  private int sum = 0;

  public void calculate() {
    setSum(getSum() + 1);
  }
}

如果我们在多线程的环境中调用这个calculate方法:

@Test
  public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods summation = new SynchronizedMethods();

    IntStream.range(0, 1000)
        .forEach(count -> service.submit(summation::calculate));
    service.shutdown();
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);

    assertEquals(1000, summation.getSum());
  }

按照上面的方法,我们预计要返回1000, 但是实际上基本不可能得到1000这个值,因为在多线程环境中,对同一个资源进行同时操作带来的不利影响。

那我们怎么才能够建线程安全的环境呢?

Synchronized关键词

java提供了多种线程安全的方法,本文主要讲解Synchronized关键词,Synchronized关键词可以有很多种形式:

  1. Instance methods
  2. Static methods
  3. Code blocks

当我们使用synchronized时,java会在相应的对象上加锁,从而在同一个对象等待锁的方法都必须顺序执行,从而保证了线程的安全。

Synchronized Instance Methods

Synchronized关键词可以放在实例方法的前面:

public synchronized void synchronisedCalculate() {
    setSum(getSum() + 1);
  }

看下调用结果:

@Test
public void givenMultiThread_whenMethodSync() {
  ExecutorService service = Executors.newFixedThreadPool(3);
  SynchronizedMethods method = new SynchronizedMethods();

  IntStream.range(0, 1000)
   .forEach(count -> service.submit(method::synchronisedCalculate));
  service.awaitTermination(1000, TimeUnit.MILLISECONDS);

  assertEquals(1000, method.getSum());
}

这里synchronized将会锁住该方法的实例对象,多个线程中只有获得该实例对象锁的线程才能够执行。

Synchronized Static Methods

Synchronized关键词也可以用在static方法前面:

public static synchronized void syncStaticCalculate() {
    staticSum = staticSum + 1;
  }

Synchronized放在static方法前面和实例方法前面锁住的对象不同。放在static方法前面锁住的对象是这个Class本身,因为一个Class在JVM中只会存在一个,所以不管有多少该Class的实例,在同一时刻只会有一个线程可以执行该放方法。

@Test
  public void givenMultiThread_whenStaticSyncMethod() throws InterruptedException {
    ExecutorService service = Executors.newCachedThreadPool();

    IntStream.range(0, 1000)
        .forEach(count ->
            service.submit(SynchronizedMethods::syncStaticCalculate));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);

    assertEquals(1000, SynchronizedMethods.staticSum);
  }

Synchronized Blocks

有时候,我们可能不需要Synchronize整个方法,而是同步其中的一部分,这时候,我们可以使用Synchronized Blocks:

public void performSynchronizedTask() {
    synchronized (this) {
      setSum(getSum() + 1);
    }
  }

我们看下怎么测试:

@Test
  public void givenMultiThread_whenBlockSync() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods synchronizedBlocks = new SynchronizedMethods();

    IntStream.range(0, 1000)
        .forEach(count ->
            service.submit(synchronizedBlocks::performSynchronizedTask));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);

    assertEquals(1000, synchronizedBlocks.getSum());
  }

上面我们同步的是实例,如果在静态方法中,我们也可以同步class:

public static void performStaticSyncTask(){
    synchronized (SynchronizedMethods.class) {
      staticSum = staticSum + 1;
    }
  }

我们看下怎么测试:

@Test
  public void givenMultiThread_whenStaticSyncBlock() throws InterruptedException {
    ExecutorService service = Executors.newCachedThreadPool();

    IntStream.range(0, 1000)
        .forEach(count ->
            service.submit(SynchronizedMethods::performStaticSyncTask));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);

    assertEquals(1000, SynchronizedMethods.staticSum);
  }

本文的例子可以参考 https://github.com/ddean2009/learn-java-concurrency/tree/master/Synchronized

ps:synchronized关键字的作用域有二种:

  1)是某个对象实例内,synchronized aMethod(){} 可以防止多个线程同时 访问这个对象的synchronized方法 (如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法 )。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
总的来说,这种情况,锁就是这个方法所在的对象

  2)是某个类的范围,synchronized static aStaticMethod{} 防止多个线程同时访问这个类中的synchronized static 方法 。它可以对类的所有对象实例起作用。此时锁就是这个class

总结

到此这篇关于java并发中的Synchronized关键词的文章就介绍到这了,更多相关java Synchronized关键词内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 批量将现有Jar包上传到Maven私服

    批量将现有Jar包上传到Maven私服

    今天小编就为大家分享一篇关于批量将现有Jar包上传到Maven私服,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 在java中用Scanner类读入单个字符的方法

    在java中用Scanner类读入单个字符的方法

    今天小编就为大家分享一篇在java中用Scanner类读入单个字符的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • SpringMVC视图作用详解

    SpringMVC视图作用详解

    这篇文章主要介绍了springMVC中的视图与视图解析器,springMVC视图的种类很多,默认有转发视图和重定向视图,本文就每一种视图给大家详细介绍,需要的朋友可以参考下
    2022-11-11
  • Java结合JS实现URL编码与解码

    Java结合JS实现URL编码与解码

    这篇文章介绍了Java结合JS实现URL编码与解码的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • spring boot thymeleaf 图片上传web项目根目录操作步骤

    spring boot thymeleaf 图片上传web项目根目录操作步骤

    这篇文章主要介绍了spring boot thymeleaf 图片上传web项目根目录步骤,本文给大家提到了thymeleaf的基础知识,需要的朋友可以参考下
    2018-03-03
  • SpringBoot环境搭建图文教程

    SpringBoot环境搭建图文教程

    这篇文章主要为大家详细介绍了SpringBoot环境搭建图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Java隐藏特性之双括号初始化详解

    Java隐藏特性之双括号初始化详解

    Java 语言拥有许多隐藏而强大的特性,其中之一是双括号初始化,这篇文章将详细介绍双括号初始化的概念、用法和示例代码,希望对大家有所帮助
    2023-12-12
  • Springboot @Configuration @bean注解作用解析

    Springboot @Configuration @bean注解作用解析

    这篇文章主要介绍了springboot @Configuration @bean注解作用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Springboot采用jasypt加密配置的项目实践

    Springboot采用jasypt加密配置的项目实践

    本文主要介绍了在Spring Boot项目中使用Jasypt对配置文件中的敏感信息进行加密,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • Mybatis实现动态建表代码实例

    Mybatis实现动态建表代码实例

    这篇文章主要介绍了Mybatis实现动态建表代码实例,解释一下,就是指根据传入的表名,动态地创建数据库表,以供后面的业务场景使用,
    而使用 Mybatis 的动态 SQL,就能很好地为我们解决这个问题,需要的朋友可以参考下
    2023-10-10

最新评论