经典 8 锁问题

示例代码

1、资源类

import java.util.concurrent.TimeUnit;

public class Phone {
	// 此方法根据题目需要添加 static
	public synchronized void sendSMS() {
		// 使用 TimeUnit 工具类执行 sleep 方法,比 Thread.sleep(毫秒),
		// 方便控制输入的时间单位
            // 根据题目要求设置该方法的休眠时间
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("---- sendSMS");
	}
	
	// 此方法根据题目需要添加 static
	public synchronized void sendEmail() {
		System.out.println("---- sendEmail");
	}
	
	public void getHello() {
		System.out.println("---- getHello");
	}
}

2、线程

public class Main {
	public static void main(String[] args) throws Exception {
		Phone phone = new Phone();
		Phone phone2 = new Phone();
		
		new Thread(() -> {
			phone.sendSMS();
		}, "Thread_01").start();
		
		Thread.sleep(100);
		
		new Thread(() -> {
			// 根据题目需要取消相应的注释
			phone.sendEmail();
			// phone.getHello();
			// phone2.sendEmail();
		}, "Thread_02").start();
	}
}

问题

  1. 标准访问,即同一对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.sendEmail(),打印结果为?
  2. 两个线程调用同一个对象的方法(对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.sendEmail()),在方法 sendSMS 中休眠 4 秒,打印结果为?
  3. 两个线程调用同一个对象的方法(对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.getHello()(普通方法)),在方法 sendSMS 中休眠 4 秒,打印结果为?
  4. 线程 Thread_01 调用对象 phone 的方法 sendSMS()(在方法内休眠 4 秒),线程 Thread_02 调用对象 phone2 的方法 sendEmail(),此时打印结果为?
  5. 将类 Phone 中的 sendSMS() 和 sendEmail() 方法更改为静态方法,分别由两个线程调用,此时打印结果为?
  6. 将类 Phone 中的 sendSMS() 和 sendEmail() 方法更改为静态方法,创建两个对象 phone 和 phone2,分别由两个线程调用,此时打印结果为?
  7. 将类 Phone 中的 sendSMS() 方法改为静态方法,sendEmail() 方法是非静态方法,创建一个对象 phone,分别由两个线程调用,此时打印结果为?
  8. 将类 Phone 中的 sendSMS() 方法改为静态方法,sendEmail() 方法是非静态方法,创建对象 phone、phone2,分别由两个线程调用,此时打印结果为?

结果分析

【问题一】 标准访问,即同一对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.sendEmail(),打印结果为?

【结果】
---- sendSMS
---- sendEmail
【解析】
因两个同步方法为非静态同步方法,即同步方法的锁为当前对象(this),而且两个线程使用的是同一个对象 phone,所以两个线程共用同一把锁。因为在创建并启动两个线程中间有 Thread.sleep(100);,故 Thread_01 先获取对象锁,所以先输出 Thread_01 的结果,详见如下动图:
在这里插入图片描述


【问题二】两个线程调用同一个对象的方法(对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.sendEmail()),在方法 sendSMS 中休眠 4 秒,打印结果为?

【结果】
(等待 4 秒后)
---- sendSMS
---- sendEmail
【解析】
和问题一相同,详见如下动图:
在这里插入图片描述


【问题三】两个线程调用同一个对象的方法(对象 phone,Thread_01 ==> phone.sendSMS(),Thread_02 ==> phone.getHello()(普通方法)),在方法 sendSMS 中休眠 4 秒,打印结果为?

【结果】
---- getHello
---- sendSMS (等待 4 秒后)
【解析】
非静态同步方法的对象锁对普通方法(没有锁)是无效的,因在 sendSMS 中使 Thread_01 休眠 4 秒,故先输入普通方法的结果。


【问题四】线程 Thread_01 调用对象 phone 的方法 sendSMS()(在方法内休眠 4 秒),线程 Thread_02 调用对象 phone2 的方法 sendEmail(),此时打印结果为?

【结果】
---- sendEmail
---- sendSMS (等待 4 秒后)
【解析】
同步方法为非静态同步方法,即同步方法的锁是对象锁。此时两个线程分别调用的是两个对象的方法,两个线程的同步方法使用的不是同一把锁,故两个线程互相无影响。因 Thread_01 休眠 4 秒,所以先输出 Thread_02 的结果,详见如下动图:
在这里插入图片描述


【问题五】将类 Phone 中的 sendSMS() 和 sendEmail() 方法更改为静态方法,分别由两个线程调用,此时打印结果为?

【结果】
(等待 4 秒后)
---- sendSMS
---- sendEmail
【解析】
两个同步方法均是静态同步方法,故同步方法的锁是类锁(Phone.class),此时两个线程调的同步方法使用的是同一把锁,同理问题一,故先输出 Thread_01 的结果,原理动画同问题二。


【问题六】将类 Phone 中的 sendSMS() 和 sendEmail() 方法更改为静态方法,创建两个对象 phone 和 phone2,分别由两个线程调用,此时打印结果为?

【结果】
(等待 4 秒后)
---- sendSMS
---- sendEmail
【解析】
同理问题五

注:类锁:只要是同一个类,无论有多少个对象,静态同步方法均使用同一把锁


【问题七】将类 Phone 中的 sendSMS() 方法改为静态方法,sendEmail() 方法是非静态方法,创建一个对象 phone,分别由两个线程调用,此时打印结果为?

【结果】
---- sendEmail
---- sendSMS (等待 4 秒后)
【解析】
sendSMS() 是静态同步方法,使用的类锁,sendEmail() 是非静态同步方法,使用的是对象锁。虽然两个线程的同步方法由一个对象调用,但是两个线程的同步方法不是使用的同一把锁,所以两个线程互不影响,因 Thread_01 休眠 4 秒,故 Thread_02 先输出结果,详见如下动图:
在这里插入图片描述


【问题八】将类 Phone 中的 sendSMS() 方法改为静态方法,sendEmail() 方法是非静态方法,创建对象 phone、phone2,分别由两个线程调用,此时打印结果为?

【结果】
---- sendEmail
---- sendSMS (等待 4 秒后)
【解析】
sendSMS() 是静态同步方法,使用的类锁,sendEmail() 是非静态同步方法,使用的是对象锁。两个线程的同步方法由两个对象调用,两个同步方法不是同一把锁,所以两个线程互不影响,因 Thread_01 休眠 4 秒,故 Thread_02 先输出结果,详见问题八动图。


总结

八锁问题考查的知识点如下:

  • Synchronized 静态同步方法使用的锁是类锁(Xxxx.class),非静态同步方法使用的是对象锁(this)
  • 多个线程中同一对象的非静态同步方法使用的是同一把对象锁,同一个类的静态方法使用的是同一把类锁
  • 类锁和对象锁相互无效
  • 锁对普通方法无效
  • 判断多个线程是否使用的同一把锁,若是同一把锁,根据获取锁的顺序执行,若不是同一把锁,线程之间互不影响
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值