synchronized 是 Java 语言中处理并发问题的一种常用手段,它也被我们亲切的称之为“Java 内置锁”,由此可见其地位之高。然而 synchronized 却有着多种用法,当它修饰不同对象时,其意义也是不同的,下面我们一起来看。

synchronized 用法

synchronized 可以用来修饰普通方法、静态方法和代码块

① 修饰普通方法

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

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

② 修饰静态方法

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

当 synchronized 修饰静态的方法时,其作用的范围是整个方法,作用对象是调用这个类的所有对象。

③ 修饰代码块

为了减少锁的粒度,我们可以选择在一个方法中的某个部分使用 synchronized 来修饰(一段代码块),从而实现对一个方法中的部分代码进行加锁,实现代码如下:

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

以上代码在执行时,被修饰的代码块称为同步语句块,其作用范围是大括号“{}”括起来的代码块,作用的对象是调用这个代码块的对象。

但以上代码,除了可以加锁 class 之外,还可以加锁 this,具体示例如下:

public void classMethod() throws InterruptedException {
// 前置处理代码...
synchronized (this) {
// ......
}
// 后置处理代码...
}

那问题来了,使用 synchronized 加锁 this 和 class 的区别是什么?不都是加锁同一个类吗?

答案还真不是,加锁 this 和 class 区别还是很大的。下面我们通过以下 4 个示例,来看二者之间的区别。

1.加锁 class 共享一个类实例

首先,我们创建 5 个线程,调用同一个对象下 synchronized 加锁的 class 代码,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit; public class SynchronizedExample { public static void main(String[] args) {
// 创建当前类实例
final SynchronizedExample example = new SynchronizedExample();
// 创建 5 个线程执行任务
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 调用 synchronized 修饰的 class 方法
example.classMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
} /**
* synchronized 修饰的 class 方法
* @throws InterruptedException
*/
public void classMethod() throws InterruptedException {
synchronized (SynchronizedExample.class) {
System.out.println(String.format("当前执行线程:%s,执行时间:%s",
Thread.currentThread().getName(), new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
}

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



从上述结果可以看出,这 5 个线程共享的是同一把锁。

2.加锁 class 创建多个实例

接下来,我们创建 5 个线程,调用不同对象下 synchronized 加锁的 class 代码,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit; public class SynchronizedExample { public static void main(String[] args) {
// 创建 5 个线程执行任务
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 创建类实例
SynchronizedExample example = new SynchronizedExample();
// 调用 synchronized 修饰的 class 方法
example.classMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
} /**
* synchronized 修饰的 class 方法
* @throws InterruptedException
*/
public void classMethod() throws InterruptedException {
synchronized (SynchronizedExample.class) {
System.out.println(String.format("当前执行线程:%s,执行时间:%s",
Thread.currentThread().getName(), new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
}

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



从上述结果可以看出,虽然是不同的对象,但它们使用的仍然是同一把锁。

3.加锁 this 共享一个类实例

接下来,我们创建 5 个线程,调用 synchronized 加锁 this 的示例。首先我们这 5 个线程调用同一个对象的加锁方法,示例代码如下:

import java.util.Date;
import java.util.concurrent.TimeUnit; public class SynchronizedExample { public static void main(String[] args) {
// 创建当前类实例
final SynchronizedExample example = new SynchronizedExample();
// 创建 5 个线程执行任务
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 调用 synchronized 修饰的 this 方法
example.thisMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
} /**
* synchronized 修饰的 this 方法
* @throws InterruptedException
*/
public void thisMethod() throws InterruptedException {
synchronized (this) {
System.out.println(String.format("当前执行线程:%s,执行时间:%s",
Thread.currentThread().getName(), new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
}

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



从上述结果可以看出,以上线程使用的都是同一把锁。

4.加锁 this 创建多个类实例

最后一个示例最为特殊,我们使用 synchronized 加锁 this,让这 5 个线程调用各自创建对象的方法,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit; public class SynchronizedExample { public static void main(String[] args) {
// 创建 5 个线程执行任务
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 创建(多个)类实例
SynchronizedExample example = new SynchronizedExample();
// 调用 synchronized 修饰的 this 方法
example.thisMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
} /**
* synchronized 修饰的 this 方法
* @throws InterruptedException
*/
public void thisMethod() throws InterruptedException {
synchronized (this) {
System.out.println(String.format("当前执行线程:%s,执行时间:%s",
Thread.currentThread().getName(), new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
}

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



从上述结果可以看出,当使用 synchronized 加锁 this 时,如果线程调用的不是同一个对象,那么这些线程之间使用的锁都是自己独立的锁,这个结果就和 synchronized 加锁 class 的结果完全不同了。

总结

通过以上 4 个示例我们可以得出结论,当使用 synchronized 加锁 class 时,无论共享一个对象还是创建多个对象,它们用的都是同一把锁,而使用 synchronized 加锁 this 时,只有同一个对象会使用同一把锁,不同对象之间的锁是不同的

本系列推荐文章

  1. 并发第一课:Thread 详解
  2. Java中用户线程和守护线程区别这么大?
  3. 深入理解线程池 ThreadPool
  4. 线程池的7种创建方式,强烈推荐你用它...
  5. 池化技术到达有多牛?看了线程和线程池的对比吓我一跳!
  6. 并发中的线程同步与锁
  7. volatile 和 synchronized 的区别
  8. 轻量级锁一定比重量级锁快吗?
  9. 这样终止线程,竟然会导致服务宕机?
  10. SimpleDateFormat线程不安全的5种解决方案!
  11. ThreadLocal不好用?那是你没用对!
  12. ThreadLocal内存溢出代码演示和原因分析!
  13. Semaphore自白:限流器用我就对了!
  14. CountDownLatch:别浪,等人齐再团!
  15. CyclicBarrier:人齐了,司机就可以发车了!

关注公号「Java中文社群」查看更多有意思、涨知识的 Java 并发文章。

synchronized 加锁 this 和 class 的区别!的更多相关文章

  1. 对象中属性 加锁 用:volatile 关键词修饰 而 不用 synchronized 加锁

    一个对象中有一个状态 属性,现在业务需求 存在多线程来修改 和 拿去 这个状态 的值,这种情况如果加锁怎么加? 一种是 在 set 和get 这个状态的 方法那加 synchronized . 还有一 ...

  2. (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别

    场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...

  3. Java锁Synchronized对象锁和类锁区别

    java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...

  4. 5分钟搞清楚Synchronized和Lock的概念与区别

    前言 并发编程中,锁是经常需要用到的,今天我们一起来看下Java中的锁机制:synchronized和lock. Synchronized 和 Lock的概念 Synchronized 是Java 并 ...

  5. 线程的同步控制synchronized和lock的对比和区别

     转载. https://blog.csdn.net/wu1226419614/article/details/73740899 我们在面试的时候,时常被问到如何保证线程同步已经对共享资源的多线程编程 ...

  6. synchronized修饰方法和对象的区别

    使用synchronized(object) { 代码块.... } 能对代码块进行加锁,不允许其他线程访问,其的作用原理是:在object内有一个变量,当有线程进入时,判断是否为0,如果为0,表示可 ...

  7. synchronized 加锁Integer对象(数据重复)详解

    场景描述:多线程输出1到100,对静态Integer对象加锁,synchronized代码块中操作Integer对象,发生线程安全问题(数据重复) 代码: public class MyRunnabl ...

  8. Java并发——synchronized和ReentrantLock的联系与区别

    0 前言 本文通过使用synchronized以及Lock分别完成"生产消费场景",再引出两种锁机制的关系和区别,以及一些关于锁的知识点. 本文原创,转载请注明出处:http:// ...

  9. No.1.测试Synchronized加锁String字符串后的多线程同步状况

    测试目的描述 Synchronized关键字锁定String字符串可能会带来严重的后果, 尽量不要使用 synchronized(String a) 因为JVM中,因为字符串常量池具有缓冲功能! 接下 ...

随机推荐

  1. 聊一聊 Spring Boot 中 RESTful 接口设计规范

    在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性.接口的稳定性.接口的跨域性.接口的协议规则.接口的路径规则.接口单一原则.接口过滤和接口组合等诸多因素,本篇文章将简要分 ...

  2. [Abp vNext 源码分析] - 20. 电子邮件与短信支持

    一.简介 ABP vNext 使用 Volo.Abp.Sms 包和 Volo.Abp.Emailing 包将短信和电子邮件作为基础设施进行了抽象,开发人员仅需要在使用的时候注入 ISmsSender ...

  3. Java并发之ReentrantLock源码解析(一)

    ReentrantLock ReentrantLock是一种可重入的互斥锁,它的行为和作用与关键字synchronized有些类似,在并发场景下可以让多个线程按照一定的顺序访问同一资源.相比synch ...

  4. java特点了解及JDK初谈

    java特性: 1.跨平台:主要是指字节码文件可以在任何具有Java虚拟机的计算机或者电子设备上运行,Java虚拟机中的Java解释器负责将字节码文件解释成为特定的机器码进行运行. 2.简单:相比与C ...

  5. ES6学习笔记之 let与const

    在js中,定义变量时要使用var操作符,但是var有许多的缺点,如:一个变量可以重复声明.没有块级作用域.不能限制修改等. //缺点1:变量可以重复声明 var a=1; var a=2; conso ...

  6. js笔记20

    1.DOM零级事件元素绑定多个click,最后只执行最后一个click    DOM二级事件绑定多个click,都要执行 注意当绑定的多个事件名,函数名,事件发生阶段三者完全一样时,才执行最后一个 第 ...

  7. Pytest学习笔记9-失败重跑

    前言 在进行自动化测试的过程中,我们一定会有这样的需求:希望失败的用例可以自动重跑 在pytest中,提供了pytest-rerunfailures插件可以实现自动重跑的效果 插件安装 pip命令安装 ...

  8. 35、cobbler自动化安装操作系统

    35.1.cobbler介绍: Cobbler是独立的,不需要先安装Kickstart然后再安装Cobbler: Cobbler是一个Linux服务器安装的服务,可以通过网络启动(PXE)的方式来快速 ...

  9. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  10. 【知识点】C++的日志框架

    常用的 C++ 日志框架 一.日志框架 一个经过专门设计的实用程序,用于规范应用程序的日志记录过程,就是日志框架.   日志框架可以自己编写(需要一定的能力哦),也可以由第三方(例如:log4cplu ...