java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java synchronized 锁

Java synchronized 锁的 8 个经典问题小结

作者:Knight_AL

本文通过8个实验探讨Java中synchronized关键字的不同锁机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、前言

synchronized 的“锁的 8 个问题”几乎是并发面试必考。题目表面是在问“先打印短信还是邮件”,本质考察的是:对象锁 vs 类锁、同一对象 vs 不同对象、静态同步 vs 普通同步方法 等关键概念。下面用 8 个最典型的小实验,把底层锁定逻辑讲清楚。

二、代码基础模板

资源类 Phone:包含两个同步方法(锁 this)和一个普通方法(不加锁)。

class Phone {

    // 发送短信(同步方法:锁 this)
    public synchronized void sendSMS() {
        System.out.println(Thread.currentThread().getName() + " → 发送短信");
    }

    // 发送邮件(同步方法:锁 this)
    public synchronized void sendEmail() {
        System.out.println(Thread.currentThread().getName() + " → 发送邮件");
    }

    // 普通方法(无锁)
    public void getHello() {
        System.out.println(Thread.currentThread().getName() + " → hello");
    }
}

简单试跑:

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(phone::sendSMS, "AA").start();
        new Thread(phone::sendEmail, "BB").start();
    }
}

三、锁的 8 个问题(逐一验证)

下列 8 个小实验通过微调代码,验证不同锁语义。为更稳定观察先后顺序,部分用到小延迟。

(1)标准访问:先短信还是邮件?

class Phone {
    public synchronized void sendSMS() { System.out.println("发送短信"); }
    public synchronized void sendEmail() { System.out.println("发送邮件"); }
}

public class Lock8_1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(phone::sendSMS, "AA").start();
        new Thread(phone::sendEmail, "BB").start();
    }
}

现象:

发送短信
发送邮件

结论: 两个同步方法、同一对象 → 同一把对象锁(this),谁先拿锁谁先执行(通常按启动时机)。

(2)短信方法内停 4 秒:谁先?

class Phone {
    public synchronized void sendSMS() {
        try { Thread.sleep(4000); } catch (InterruptedException ignored) {}
        System.out.println("发送短信");
    }
    public synchronized void sendEmail() { System.out.println("发送邮件"); }
}

public class Lock8_2 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(phone::sendSMS, "AA").start();
        // 小延迟,确保 AA 先启动拿到锁
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        new Thread(phone::sendEmail, "BB").start();
    }
}

现象:

发送短信
发送邮件

结论: 同一对象的两个同步方法互斥AA 持锁期间 BB 必须等待 → 串行。

(3)普通方法 + 同步方法:谁先?

class Phone {
    public synchronized void sendSMS() {
        try { Thread.sleep(4000); } catch (InterruptedException ignored) {}
        System.out.println("发送短信");
    }
    public void getHello() { System.out.println("hello"); }
}

public class Lock8_3 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(phone::sendSMS, "AA").start();
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        new Thread(phone::getHello, "BB").start();
    }
}

现象:

hello
发送短信

结论: 普通方法不加锁,不受同步影响 → 直接执行,通常先于同步方法的输出。

(4)两部手机(两个对象),各自同步方法

public class Lock8_4 {
    public static void main(String[] args) {
        Phone p1 = new Phone();
        Phone p2 = new Phone();
        new Thread(p1::sendSMS, "AA").start();
        new Thread(p2::sendEmail, "BB").start();
    }
}

现象:

发送邮件
发送短信

结论: 每个实例都有独立的对象锁,不同对象互不干扰 → 可以并行。

(5)两个静态同步方法,一部手机

class Phone {
    public static synchronized void sendSMS() {
        try { Thread.sleep(4000); } catch (InterruptedException ignored) {}
        System.out.println("发送短信");
    }
    public static synchronized void sendEmail() { System.out.println("发送邮件"); }
}

public class Lock8_5 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(Phone::sendSMS, "AA").start();
        new Thread(Phone::sendEmail, "BB").start();
    }
}

现象:

发送短信
发送邮件

结论: 静态同步方法锁的是类对象 Phone.class(类锁),全类唯一 → 串行。

(6)两个静态同步方法,两部手机

public class Lock8_6 {
    public static void main(String[] args) {
        Phone p1 = new Phone();
        Phone p2 = new Phone();
        new Thread(Phone::sendSMS, "AA").start();
        new Thread(Phone::sendEmail, "BB").start();
    }
}

现象:

发送短信
发送邮件

结论: 仍是同一个 Phone.class 的锁,和多少实例无关 → 串行。

(7)一个静态同步 + 一个普通同步,一部手机

class Phone {
    public static synchronized void sendSMS() {
        try { Thread.sleep(4000); } catch (InterruptedException ignored) {}
        System.out.println("发送短信");
    }
    public synchronized void sendEmail() { System.out.println("发送邮件"); }
}

public class Lock8_7 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(Phone::sendSMS, "AA").start();
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        new Thread(phone::sendEmail, "BB").start();
    }
}

现象:

发送邮件
发送短信

结论: 类锁(Phone.class)与对象锁(this)是两把不同的锁 → 可并行。

(8)一个静态同步 + 一个普通同步,两部手机

public class Lock8_8 {
    public static void main(String[] args) {
        Phone p1 = new Phone();
        Phone p2 = new Phone();
        new Thread(Phone::sendSMS, "AA").start();
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        new Thread(p2::sendEmail, "BB").start();
    }
}

现象:

发送邮件
发送短信

结论: 类锁(Phone.class)与某个实例的对象锁(p2.this)互不影响 → 并行。

四、八大结论一览

编号场景描述锁对象是否同一锁执行特征
1同一对象两个同步方法对象锁(this)✅ 是串行
2同一对象,一个方法内睡眠对象锁(this)✅ 是串行
3同一对象,一个同步 + 一个普通方法对象锁 + 无锁❌ 否普通先
4两个对象,各自同步方法两个对象锁❌ 否并行
5一对象,两个静态同步方法类锁(Phone.class✅ 是串行
6两对象,两个静态同步方法类锁(Phone.class✅ 是串行
7一对象:一个静态同步 + 一个普通同步类锁 + 对象锁❌ 否并行
8两对象:一个静态同步 + 一个普通同步类锁 + 对象锁❌ 否并行

五、核心知识小结

六、要点

以上 8 个实验覆盖了 synchronized 的主流锁粒度考点。理解“锁定谁”这一件事,就能解释为什么有些代码会阻塞,有些能并行。
牢记:锁不是给代码加的,而是给对象加的。

到此这篇关于Java synchronized 锁的 8 个经典问题小结的文章就介绍到这了,更多相关Java synchronized 锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文