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. NOIP模拟测试29「爬山·学数数·七十和十七」

    爬山题解不想写了 学数数 离散化然后找到以每一个值为最大值的连续子段有多少个,然后开个桶维护 那么怎么找以每一个值为最大值的连续子段个数 方法1(我的极笨的方法) 考试时我的丑陋思路, 定义极左值为左 ...

  2. 测试MySQL锁的问题

    测试MySQL锁的问题 目录 测试MySQL锁的问题 1 Record Lock 2 Next-Key Lock 2 死锁测试 InnoDB支持三种行锁: Record Lock:单个行记录上面的锁 ...

  3. Jrebel、IDEA的激活与Springloaded使用

    又有很长一段时间没写了,这次这篇随笔主要是分享下Jrebel与IDEA的激活方法以及推荐下Jrebel的替代工具Springloaded. 先来说下Jrebel的激活方法吧,之前有同事遇到了Jrebe ...

  4. 基于Ubuntu下以Docker方式gitlab软件的部署

    基于Ubuntu下以Docker方式gitlab软件的部署 目录 基于Ubuntu下以Docker方式gitlab软件的部署 1.安装Docker Compose 1.1 下载curl 1.2 安装c ...

  5. oscp-缓冲区溢出(持续更新)

    环境准备 Windows7虚拟机(我选了IE8,其实也没什么关系) 微软官方下载地址 These virtual machines expire after 90 days. We recommend ...

  6. Gym - 101128E Wooden Signs DP

    题目大意: 一共n块木板,前两个数给出最底下木块的两个端点,后面n-1个数给出第i层的一个固定端点,问你木块的所有放置情况. 分析: 状态: d[i][j]表示第i个木块,第i-1块木板的未固定端点为 ...

  7. Java:java -jar命令讲解

    1. 当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 #正常启动jar包 java -jar XXX.jar#当前ssh窗口被锁定,可按CTRL + C打断程序运行, ...

  8. 使用RestTemplate发送post请求,请求头中封装参数

    最近使用RestTemplate发送post请求,遇到了很多问题,如转换httpMessage失败.中文乱码等,调了好久才找到下面较为简便的方法: RestTemplate restTemplate ...

  9. PHP实现的解汉诺塔问题算法示例

    问题描述: 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏.该游戏是在一块铜板装置上,有三根杆(编号A.B.C),在A杆自下而上.由大到小按顺序放置64个金盘(如下图).游戏的目标:把A杆 ...

  10. PHP7与php5

    php在2015年12月03日发布了7.0正式版,带来了许多新的特性,以下是不完全列表: 性能提升:PHP7比PHP5.6性能提升了两倍. Improved performance: PHP 7 is ...