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. Redis不是只有get set那么简单

    我以前还没接触Redis的时候,听到大数据组的小伙伴在讨论Redis,觉得这东西好高端,要是哪天我们组也可以使用下Redis就好了,好长一段时间后,我们项目中终于引入了Redis这个技术,我用了几下, ...

  2. PRVF-0002: could not retrieve local nodename报错解决

     

  3. 『无为则无心』Python基础 — 5、Python开发工具的安装与使用

    目录 1.Pycharm下载 2.Pycharm安装 3.PyCharm界面介绍 4.基本使用 (1)新建Python项目 (2)编写Python代码 (3)执行代码查看结果 (4)设置PyCharm ...

  4. RobotFramework + Python 自动化入门 二 (关键字)

    在<RobotFramwork + Python 自动化入门 一>中,完成了Robot环境搭建及测试脚本的创建和执行. 本节,对RobotFramework的关键字使用和查看源码进行介绍. ...

  5. 第2章:Kubernetes核心概念

    Kubernetes是Google在2014年开源的一个容器集群管理系统,Kubernetes简称K8S. Kubernetes用于容器化应用程序的部署,扩展和管理,目标是让部署容器化应用简单高效. ...

  6. 10、mysql增量恢复实战

    10.1.实验数据: mysql> select * from test; +----+------+------+ | id | name | age | +----+------+----- ...

  7. 并发王者课-铂金9:互通有无-Exchanger如何完成线程间的数据交换

    欢迎来到<并发王者课>,本文是该系列文章中的第22篇,铂金中的第9篇. 在前面的文章中,我们已经介绍了ReentrantLock,CountDownLatch,CyclicBarrier, ...

  8. hdu 2842 Chinese Rings 矩阵快速幂

    分析: 后面的环能不能取下来与前面的环有关,前面的环不被后面的环所影响.所以先取最后面的环 设状态F(n)表示n个环全部取下来的最少步数 先取第n个环,就得使1~n-2个环属于被取下来的状态,第n-1 ...

  9. CRM系统对企业管理的作用有多大?

    随着市场经济的发展,对任何行业的企业来说,客户都是非常重要的一个部分.CRM系统帮助企业做到以客户为中心,它可以根据客户的具体要求进行跟进和反馈,在很大程度上提高公司的客户服务水平和客户满意度,进而提 ...

  10. UI自动化学习笔记- UnitTest单元测试框架详解

    一.UnitTest基本使用 1. UnitTest框架 1.1 什么是框架 说明: 框架英文单词frame 为解决一类事情的功能集合 1.2什么是UnitTest框架 概念:UnitTest是pyt ...