synchronized 加锁 this 和 class 的区别!

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

[[419166]]

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

synchronized 用法

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

① 修饰普通方法 

  1. /**  
  2.  * synchronized 修饰普通方法  
  3.  */  
  4. public synchronized void method() {  
  5.     // .......  

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

② 修饰静态方法 

  1. /**  
  2.  * synchronized 修饰静态方法  
  3.  */  
  4. public static synchronized void staticMethod() {  
  5.     // .......  

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

③ 修饰代码块

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

  1. public void classMethod() throws InterruptedException {  
  2.     // 前置代码...    
  3.     // 加锁代码  
  4.     synchronized (SynchronizedExample.class) {  
  5.         // ......  
  6.     }  
  7.      // 后置代码... 

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

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

  1. public void classMethod() throws InterruptedException {  
  2.     // 前置处理代码...  
  3.     synchronized (this) {  
  4.         // ......  
  5.     }  
  6.     // 后置处理代码...  

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

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

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

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

  1. import java.util.Date;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class SynchronizedExample {  
  4.     public static void main(String[] args) {  
  5.         // 创建当前类实例  
  6.         final SynchronizedExample example = new SynchronizedExample();  
  7.         // 创建 5 个线程执行任务  
  8.         for (int i = 0; i < 5; i++) {  
  9.             new Thread(new Runnable() {  
  10.                 @Override  
  11.                 public void run() {  
  12.                     try {  
  13.                         // 调用 synchronized 修饰的 class 方法  
  14.                         example.classMethod();  
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 } 
  19.             }).start();  
  20.         }  
  21.     }  
  22.     /**  
  23.      * synchronized 修饰的 class 方法  
  24.      * @throws InterruptedException  
  25.      */  
  26.     public void classMethod() throws InterruptedException {  
  27.         synchronized (SynchronizedExample.class) {  
  28.             System.out.println(String.format("当前执行线程:%s,执行时间:%s",  
  29.                     Thread.currentThread().getName(), new Date()));  
  30.             TimeUnit.SECONDS.sleep(1);  
  31.         }  
  32.     }  

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

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

2.加锁 class 创建多个实例

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

  1. import java.util.Date;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class SynchronizedExample {  
  4.     public static void main(String[] args) {  
  5.         // 创建 5 个线程执行任务  
  6.         for (int i = 0; i < 5; i++) {  
  7.             new Thread(new Runnable() {  
  8.                 @Override  
  9.                 public void run() {  
  10.                     try {  
  11.                         // 创建类实例  
  12.                         SynchronizedExample example = new SynchronizedExample();  
  13.                         // 调用 synchronized 修饰的 class 方法  
  14.                         example.classMethod();  
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 }  
  19.             }).start();  
  20.         }  
  21.     }  
  22.     /**  
  23.      * synchronized 修饰的 class 方法  
  24.      * @throws InterruptedException  
  25.      */  
  26.     public void classMethod() throws InterruptedException {  
  27.         synchronized (SynchronizedExample.class) {  
  28.             System.out.println(String.format("当前执行线程:%s,执行时间:%s",  
  29.                     Thread.currentThread().getName(), new Date()));  
  30.             TimeUnit.SECONDS.sleep(1);  
  31.         }  
  32.     }  

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

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

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

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

  1. import java.util.Date;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class SynchronizedExample {  
  4.     public static void main(String[] args) {  
  5.         // 创建当前类实例  
  6.         final SynchronizedExample example = new SynchronizedExample(); 
  7.          // 创建 5 个线程执行任务  
  8.         for (int i = 0; i < 5; i++) {  
  9.             new Thread(new Runnable() {  
  10.                 @Override  
  11.                 public void run() {  
  12.                     try {  
  13.                         // 调用 synchronized 修饰的 this 方法  
  14.                         example.thisMethod();  
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 }  
  19.             }).start();  
  20.         }  
  21.     }     
  22.     /**  
  23.      * synchronized 修饰的 this 方法  
  24.      * @throws InterruptedException  
  25.      */  
  26.     public void thisMethod() throws InterruptedException {  
  27.         synchronized (this) {  
  28.             System.out.println(String.format("当前执行线程:%s,执行时间:%s",  
  29.                     Thread.currentThread().getName(), new Date()));  
  30.             TimeUnit.SECONDS.sleep(1);  
  31.         }  
  32.     }  

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

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

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

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

  1. import java.util.Date;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class SynchronizedExample {  
  4.     public static void main(String[] args) {  
  5.         // 创建 5 个线程执行任务  
  6.         for (int i = 0; i < 5; i++) {  
  7.             new Thread(new Runnable() {  
  8.                 @Override  
  9.                 public void run() {  
  10.                     try {  
  11.                         // 创建(多个)类实例  
  12.                         SynchronizedExample example = new SynchronizedExample();  
  13.                         // 调用 synchronized 修饰的 this 方法  
  14.                         example.thisMethod();  
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 }  
  19.             }).start();  
  20.         }  
  21.     }  
  22.      /**  
  23.      * synchronized 修饰的 this 方法  
  24.      * @throws InterruptedException  
  25.      */  
  26.     public void thisMethod() throws InterruptedException {  
  27.         synchronized (this) {  
  28.             System.out.println(String.format("当前执行线程:%s,执行时间:%s", 
  29.                     Thread.currentThread().getName(), new Date()));  
  30.             TimeUnit.SECONDS.sleep(1);  
  31.         }  
  32.     }  

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

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

总结

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

 

 

责任编辑:庞桂玉 来源: Hollis
相关推荐

2021-08-02 07:57:03

SynchronizeJava语言

2024-02-26 07:36:09

lockJava语言

2022-04-24 07:59:53

synchronizJVMAPI

2023-02-01 07:15:16

2020-03-10 10:25:38

volatileJava编程语言

2022-11-09 10:46:18

AQS加锁机制

2021-02-01 14:10:16

JavaClass.forNaClassLoader

2021-08-31 06:37:35

Java 语言 Java 基础

2010-08-30 10:52:39

CSSclassid

2010-08-23 14:16:17

DIVclassid

2020-09-23 09:08:05

typescript

2009-10-10 14:40:03

C++中struct

2021-01-14 08:58:12

Synchronize锁操作

2010-08-23 10:57:14

CSSclassid

2009-08-27 16:22:58

interfaceabstract cl

2009-08-27 15:48:40

interfaceabstract cl

2017-12-06 16:28:48

Synchronize实现原理

2021-03-04 08:26:17

synchronizeReentrantLojava

2025-01-02 12:59:55

Python面向对象编程type

2021-08-04 08:33:59

TypeScriptConst Readonly
点赞
收藏

51CTO技术栈公众号