软件编程
位置:首页>> 软件编程>> java编程>> Java中synchronized的几种使用方法

Java中synchronized的几种使用方法

作者:??Java中文社群????  发布时间:2023-06-19 16:33:28 

标签:Java,synchronized,用法

前言:

在 Java 语言中,保证线程安全性的主要手段是加锁,而 Java 中的锁主要有两种:synchronized 和 Lock,我们今天重点来看一下 synchronized 的几种用法。

用法简介

使用 synchronized 无需手动执行加锁和释放锁的操作,我们只需要声明 synchronized 关键字就可以了,JVM 层面会帮我们自动的进行加锁和释放锁的操作。 synchronized 可用于修饰普通方法、静态方法和代码块,接下来我们分别来看。

1、修饰普通方法

synchronized 修饰普通方法的用法如下:

/**
* synchronized 修饰普通方法
*/
public synchronized void method() {
   // ....
}

当 synchronized 修饰普通方法时,被修饰的方法被称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。

2、修饰静态方法

synchronized 修饰静态方法和修饰普通方法类似,它的用法如下:

/**
* synchronized 修饰静态方法
*/
public static synchronized void staticMethod() {
   // .......
}

当 synchronized 修饰静态方法时,其作用范围是整个程序,这个锁对于所有调用这个锁的对象都是互斥的。

所谓的互斥,指的是同一时间只能有一个线程能使用,其他线程只能排队等待。

修饰普通方法 VS 修饰静态方法

synchronized 修饰普通方法和静态方法看似相同,但二者完全不同,对于静态方法来说 synchronized 加锁是全局的,也就是整个程序运行期间,所有调用这个静态方法的对象都是互斥的,而普通方法是针对对象级别的,不同的对象对应着不同的锁,比如以下代码,同样是调用两次方法,但锁的获取完全不同

实现代码如下:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SynchronizedUsage {
   public static void main(String[] args) throws InterruptedException {
       // 创建线程池同时执行任务
       ExecutorService threadPool = Executors.newFixedThreadPool(10);

// 执行两次静态方法
       threadPool.execute(() -> {
           staticMethod();
       });
       threadPool.execute(() -> {
           staticMethod();
       });
       // 执行两次普通方法
       threadPool.execute(() -> {
           SynchronizedUsage usage = new SynchronizedUsage();
           usage.method();
       });
       threadPool.execute(() -> {
           SynchronizedUsage usage2 = new SynchronizedUsage();
           usage2.method();
       });
   }

/**
    * synchronized 修饰普通方法
    * 本方法的执行需要 3s(因为有 3s 的休眠时间)
    */
   public synchronized void method() {
       System.out.println("普通方法执行时间:" + LocalDateTime.now());
       try {
           // 休眠 3s
           TimeUnit.SECONDS.sleep(3);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }

/**
    * synchronized 修饰静态方法
    * 本方法的执行需要 3s(因为有 3s 的休眠时间)
    */
   public static synchronized void staticMethod() {
       System.out.println("静态方法执行时间:" + LocalDateTime.now());
       try {
           // 休眠 3s
           TimeUnit.SECONDS.sleep(3);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}

以上程序的执行结果如下: 

Java中synchronized的几种使用方法

 从上述结果可以看出,静态方法加锁是全局的,针对的是所有调用者;而普通方法加锁是对象级别的,不同的对象拥有的锁也不同。

3、修饰代码块

我们在日常开发中,最常用的是给代码块加锁,而不是给方法加锁,因为给方法加锁,相当于给整个方法全部加锁,这样的话锁的粒度就太大了,程序的执行性能就会受到影响,所以通常情况下,我们会使用 synchronized 给代码块加锁,

它的实现语法如下:

public void classMethod() throws InterruptedException {
   // 前置代码...
   // 加锁代码
   synchronized (SynchronizedUsage.class) {
       // ......
   }
   // 后置代码...
}

从上述代码我们可以看出,相比于修饰方法,修饰代码块需要自己手动指定加锁对象,加锁的对象通常使用 this 或 xxx.class 这样的形式来表示,比如以下代码:

// 加锁某个类
synchronized (SynchronizedUsage.class) {
   // ......
}

// 加锁当前类对象
synchronized (this) {
   // ......
}

this VS class

使用 synchronized 加锁 this 和 xxx.class 是完全不同的,当加锁 this 时,表示用当前的对象进行加锁,每个对象都对应了一把锁;而当使用 xxx.class 加锁时,表示使用某个类(而非类实例)来加锁,它是应用程序级别的,是全局生效的,

如以下代码所示:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedUsageBlock {
   public static void main(String[] args) throws InterruptedException {
       // 创建线程池同时执行任务
       ExecutorService threadPool = Executors.newFixedThreadPool(10);

// 执行两次 synchronized(this)
       threadPool.execute(() -> {
           SynchronizedUsageBlock usage = new SynchronizedUsageBlock();
           usage.thisMethod();
       });
       threadPool.execute(() -> {
           SynchronizedUsageBlock usage2 = new SynchronizedUsageBlock();
           usage2.thisMethod();
       });

// 执行两次 synchronized(xxx.class)
       threadPool.execute(() -> {
           SynchronizedUsageBlock usage3 = new SynchronizedUsageBlock();
           usage3.classMethod();
       });
       threadPool.execute(() -> {
           SynchronizedUsageBlock usage4 = new SynchronizedUsageBlock();
           usage4.classMethod();
       });
   }

/**
    * synchronized(this) 加锁
    * 本方法的执行需要 3s(因为有 3s 的休眠时间)
    */
   public void thisMethod() {
       synchronized (this) {
           System.out.println("synchronized(this) 加锁:" + LocalDateTime.now());
           try {
               // 休眠 3s
               TimeUnit.SECONDS.sleep(3);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }

/**
    * synchronized(xxx.class) 加锁
    * 本方法的执行需要 3s(因为有 3s 的休眠时间)
    */
   public void classMethod() {
       synchronized (SynchronizedUsageBlock.class) {
           System.out.println("synchronized(xxx.class) 加锁:" + LocalDateTime.now());
           try {
               // 休眠 3s
               TimeUnit.SECONDS.sleep(3);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }
}

以上程序的执行结果如下: 

Java中synchronized的几种使用方法

来源:https://juejin.cn/post/7085976394676568094

0
投稿

猜你喜欢

  • 背景在Spring boot项目开发中经常遇到需要使用枚举的场景,比如描述状态、性别、类型等相关字段。通常这些字段在数据库会以tinyint
  • What内含一个或多个maven模块的SpringBoot项目称为SpringBoot多模块项目Why便于维护 将一个大的单体项目分成多个子
  • 一、点名器需求:我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器实现步骤:创建字符缓冲输入流对象创建Arr
  • 由于for循环可以通过控制循环变量的初始值和循环结束条件来改变遍历的区间,所以在排序或者遍历的时候,利用for循环就比较简单,以下是本人学习
  • 1.使用API设置主题如下所示,在Activity中使用setThemesetTheme(R.style.MyTheme1);2.调用API
  • 本文实例讲述了Java排序算法总结之希尔排序。分享给大家供大家参考。具体分析如下:前言:希尔排序(Shell Sort)是插入排序的一种。是
  • @Value值注入及配置文件组件扫描spring配置文件对应的是父容器,springMVC配置文件产生的是子容器,前者一般配置数据源,事务,
  • Bottom SheetBottom Sheet 是 Design Support Library 23.2 版本引入的一个类似于对话框的控
  • 篇首,完全没有技术含量的帖子,高手略过,只为十几年后重新捡起的我爱好玩玩。。。起因,一个朋友说他下载了很多短视频,但只需要要其中的一小截,去
  • 本文实例为大家分享了C#实现围棋游戏的具体代码,供大家参考,具体内容如下之所以选择围棋作为大作业一方面是想挑战一下,另一方面是由于从6岁学围
  • 本文实例为大家分享了Java实现计算器设计的具体代码,供大家参考,具体内容如下需求分析目的是实现一个基于Java的可以求解带括号加减乘除表达
  • 在考虑类初始化时,我们都知道进行子类初始化时,如果父类没有初始化要先初始化子类。然而事情并没有一句话这么简单。首先看看Java中初始化触发的
  • 前言在上一篇文章中讲完了SpringMVC的大部分知识,此篇文章中主要讲解 * 。上一篇文章🚩 * 的使用是非常普遍的。例如在 OA系统中通
  • spring boot ${}占位符不起作用问题:在 pom.xml 文件里定义好属性标签,然后在 properties或者xml 中使用$
  • 一、HttpBasic模式的应用场景HttpBasic登录验证模式是Spring Security实现登录验证最简单的一种方式,也可以说是最
  • /* * 使用 C# 动态编译代码和执行 * 作者: yaob */ static void Main(string[] args) { /
  • 本文研究的主要是Spring的事务机制的相关内容,具体如下。JAVA EE传统事务机制通常有两种事务策略:全局事务和局部事务。全局事务可以跨
  • 某些情况下,我们需要在项目中对多种任务分配不同的线程池进行执行。从而通过监控不同的线程池来控制不同的任务。为了达到这个目的,需要在项目中配置
  • 本文实例为大家分享了RecyclerView实现侧滑拖拽功能的具体代码,供大家参考,具体内容如下准备ItemDragListenerpack
  • 在做Android支付的时候肯定会用到支付宝支付, 根据官方给出的demo做起来非常费劲,所以我们需要一次简单的封装。 封装的代码也很简单,
手机版 软件编程 asp之家 www.aspxhome.com