Java中synchronized的几种使用方法

目录
  • 用法简介
  • 1、修饰普通方法
  • 2、修饰静态方法
    • 修饰普通方法 VS 修饰静态方法
  • 3、修饰代码块
    • this VS class
  • 总结

前言:

在 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();
        }
    }
}

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

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

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();
            }
        }
    }
}

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

总结

synchronized 用 3 种用法,用它可以来修饰普通方法、静态方法和代码块,其中最常用的是修饰代码块,而修饰代码块时需要指定一个加锁对象,这个加锁对象通常使用 this 或 xxx.class 来表示,当使用 this 时,表示使用当前对象来加锁,而使用 class 时,表示表示使用某个类(非类对象实例)来加锁,它是全局生效的。

到此这篇关于Java中synchronized的几种使用方法的文章就介绍到这了,更多相关synchronized用法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中Synchronized的用法解析

    简单介绍 synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象: 2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象: 3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象: 4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对

  • java synchronized用法详解

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行.另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 二.然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块.

  • Java中synchronized关键字修饰方法同步的用法详解

    Java的最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问. 每一个用synchronized关键字声明的方法都是临界区.在Java中,同一个对象的临界区,在同一时间只有一个允许被访问. 静态方法则有不同的行为.用synchronized关键字声明的静态方法,同时只能够被一个执行线程访问,但是其他线程可以访问这个对象的非静态的synchronized方法.必须非常谨慎这一点,因为两个线程可以同时访问一个对象的两个不同的synchronized方法,即其中一个是静态s

  • Java开发中synchronized的定义及用法详解

    概念 是利用锁的机制来实现同步的. 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问.互斥性我们也往往称为操作的原子性. 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致. 用法 修饰静态方法: //同步静态方法 public synchronized

  • 举例讲解Java中synchronized关键字的用法

    synchronized关键字顾名思义,是用于同步互斥的作用的. 这里精简的记一下它的使用方法以及意义: 1. 当synchronized修饰 this或者非静态方法或者是一个实例的时候,所同步的锁是加在this或者实例对象引用上面的.比如a,b同为Main类的实例化对象,a调用被同步的方法,和b调用被同步的方法,没有形成互斥.但是不同线程的a对象调用被同步的方法就被互斥了. public synchronized void method(){ //-. } public void method

  • Java 中 synchronized的用法详解(四种用法)

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.这时,线程获得的是成员锁,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入. 例如: public synchronized void synMethod() { //方法体 }

  • Java中synchronized的几种使用方法

    目录 用法简介 1.修饰普通方法 2.修饰静态方法 修饰普通方法 VS 修饰静态方法 3.修饰代码块 this VS class 总结 前言: 在 Java 语言中,保证线程安全性的主要手段是加锁,而 Java 中的锁主要有两种:synchronized 和 Lock,我们今天重点来看一下 synchronized 的几种用法. 用法简介 使用 synchronized 无需手动执行加锁和释放锁的操作,我们只需要声明 synchronized 关键字就可以了,JVM 层面会帮我们自动的进行加锁和

  • java中this的n种使用方法

    this可能是几乎所有有一点面向对象思想的语言都会引用到的变量,java自然不例外.只是,this有多少种用法,我也不知道了,让我们来see see. 由简入奢! 易. 来个例子说明下: public class DebugerTest { public static void main(String[] args) { UserExample samp1 = new UserExample("amy"); System.out.println("who are u? &qu

  • java中ArrayList的两种排序方法实例

    目录 前言 1.ArrayList使用排序的初衷 2.对一个ArrayList中的数组进行排序. 3.多个ArrayList中的元素进行排序 总结 前言 由于其功能性和灵活性,ArrayList是 Java 集合框架中使用最为普遍的集合类之一.ArrayList 是一种 List 实现,它的内部用一个动态数组来存储元素,因此 ArrayList 能够在添加和移除元素的时候进行动态的扩展和缩减.你可能已经使用过 ArrayList,因此我将略过基础部分.如果你对 ArrayList 还不熟悉,你可

  • java中synchronized Lock(本地同步)锁的8种情况

    目录 lock1 lock2 lock3 lock4 lock5 lock6 lock7 lock8 Lock(本地同步)锁的8种情况总结与说明: * 题目: * 1.标准访问,请问是先打印邮件还是短信 Email * 2.email方法新增暂停4秒钟,请问是先打印邮件还是短信 Email * 3.新增普通的hello方法,请问先打印邮件还是hello hello * 4.两部手机,请问先打印邮件还是短信 SMS * 5.两个静态同步方法,1部手机,请问先打印邮件还是短信 Email * 6.两

  • java中Object类4种方法详细介绍

    目录 Object(四大方法): hashCode()方法: equals()方法: getClass()方法: toString()方法: 总结 Object(四大方法): 文章干货满满,耐性看完~~何为Object?首先先来看看官方对Object的介绍:在这里附上Java官方的查阅工具:https://docs.oracle.com/en/java/javase/17/docs/api/index.html 由官方介绍可见,object属于Java.lang包内的一个类,而且提供了很多种方法

  • 深入了解Java中Synchronized的各种使用方法

    目录 Synchronized关键字 Synchronized修饰实例方法 Synchronized修饰静态方法 Sychronized修饰多个方法 Synchronized修饰实例方法代码块 Synchronized修饰静态代码块 应该用什么对象作为锁对象 Synchronized与可见性和重排序 可见性 重排序 总结 在Java当中synchronized通常是用来标记一个方法或者代码块.在Java当中被synchronized标记的代码或者方法在同一个时刻只能够有一个线程执行被synchr

  • java中synchronized(同步代码块和同步方法)详解及区别

     java中synchronized(同步代码块和同步方法)详解及区别 问题的由来: 看到这样一个面试题: //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized (obj){} } synchronized用于解决同步问题,当有多条线程同时访问共享数据时,如果进行同步,就会发生错误,Java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他

  • Java线程池的几种实现方法及常见问题解答

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能.所以,"池"的用处就凸显出来了. 1. 为什么要使用线程池 在3.6.1节介绍的实现方式中,对每个客户都分配一个新的工作线程.当工作线程与客户通信结束,这个线程就被销毁.这种实现方式有以下不足之处: •服务器创建和销毁工作的开销( 包括所花费的时间和系统资源 )很大.这一项不用解释,可以

  • Java 中Object的wait() notify() notifyAll()方法使用

    Java 中Object的wait() notify() notifyAll()方法使用 一.前言 对于并发编程而言,除了Thread以外,对Object对象的wati和notify对象也应该深入了解其用法,虽然知识点不多. 二.线程安全基本知识 首先应该记住以下基本点,先背下来也无妨: 同一时间一个锁只能被一个线程持有 调用对象的wait()和notify()前必须持有它 三.wait()和notify()理解 3.1 wait()和notify()方法简介 wait()和notify()都是

  • 透彻理解Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别

    本文讲述了Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别.分享给大家供大家参考,具体如下: Synchronized和Static Synchronized区别 通过分析这两个用法的分析,我们可以理解java中锁的概念.一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁).实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是

随机推荐