【Android面试|华为|锁相关】- synchronize(this) 和 synchronize(class)有什么区别

华为面试官问了其中一个问题

Q: synchronize(this) 和 synchronize(class)一样么? 是否可以用synchronize(this) 来锁

A: 当使用 synchronized 加锁 class 时,无论共享一个对象还是创建多个对象,它们用的都是同一把锁,而使用 synchronized 加锁 this 时,只有同一个对象会使用同一把锁,不同对象之间的锁是不同的。

synchronized 用法

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

  1. 修饰普通方法
java 复制代码
/**
 * synchronized 修饰普通方法
 */
public synchronized void method() {
    // .......
} 

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

  1. 修饰静态方法
java 复制代码
/**
 * synchronized 修饰静态方法
 */
public static synchronized void staticMethod() {
    // .......
} 

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

  1. 修饰代码块
    为了减少锁的粒度,我们可以选择在一个方法中的某个部分使用 synchronized 来修饰(一段代码块),从而实现对一个方法中的部分代码进行加锁,实现代码如下:
java 复制代码
public void classMethod() throws InterruptedException {
    // 前置代码...
    
    // 加锁代码
    synchronized (SynchronizedExample.class) {
        // ......
    }
    
    // 后置代码...
} 

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

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

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

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

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

区别:

  1. 加锁 class 共享一个类实例
    首先,我们创建 5 个线程,调用同一个对象下 synchronized 加锁的 class 代码,具体示例如下:
java 复制代码
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 个线程共享的是同一把锁。

  1. 加锁 class 创建多个实例
    接下来,我们创建 5 个线程,调用不同对象下 synchronized 加锁的 class 代码,具体示例如下:
java 复制代码
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);
        }
    }
} 

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

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

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

java 复制代码
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);
        }
    }
} 

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

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

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

java 复制代码
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 的结果完全不同了。

相关推荐
Libraeking3 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位3 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
御承扬3 小时前
鸿蒙NDK UI之文本自定义样式
ui·华为·harmonyos·鸿蒙ndk ui
大雷神4 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地--第29篇:数据管理与备份
华为·harmonyos
发现一只大呆瓜5 小时前
虚拟列表:支持“向上加载”的历史消息(Vue 3 & React 双版本)
前端·javascript·面试
JMchen1235 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs5 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob6 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
巴德鸟6 小时前
华为手机鸿蒙4回退到鸿蒙3到鸿蒙2再回退到EMUI11 最后关闭系统更新
华为·智能手机·harmonyos·降级·升级·回退·emui
机建狂魔6 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像