多线程的8锁问题

我们现在有一个资源类Phone,它有两个基础操作,sendEmail发送邮件和sendMessage发送短信,这两个操作均加上了synchronized锁

问题:

1.标准访问,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone{
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		System.out.println("发邮件。。。");
	}
	
	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
}
//问题1
	public static void ques1() {
		Phone phone = new Phone();//一个手机资源
		//创建两个线程
		
		//线程A发送邮件
		new Thread(()->{
			phone.sendEmail();
		}, "A").start();

		
		//线程B发送短信
		new Thread(()->{
			phone.sendMessage();
		}, "B").start();
	}
执行结果

发邮件。。。
发短信。。。

结果分析

结果是哪个先打印都是有可能的,先执行start()不代表就能先执行,这和操作系统调度的顺序有关

2.邮件方法暂停4秒,请问是先打印邮件还是短信?(我们在创建线程A后先sleep100毫秒让线程A先执行)
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
}
// 问题2
	public static void ques2() {
		Phone phone = new Phone();// 一个手机资源
		// 创建两个线程

		// 线程A发送邮件
		new Thread(() -> {
			phone.sendEmail();
		}, "A").start();

		// 休眠100毫秒,保证线程A先得到锁
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 线程B发送短信
		new Thread(() -> {
			phone.sendMessage();
		}, "B").start();
	}
执行结果

等待大约4秒后显示以下两行结果

发邮件。。。
发短信。。。

结果分析

A线程先抢到这个手机资源的锁,其抢到锁后立刻睡眠4秒,而sleep是一个只释放cpu时间片而不释放所持有锁的方法,所以A线程是一边“睡着”,一边紧握着锁不让其他线程访问这个手机资源里的同步操作,等待4秒后A线程醒过来,继续发邮件,发完后释放锁,B线程得到锁,打印出发短信

3.新增一个普通方法hello(),请问是先打印邮件还是hello?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
	
	//普通方法,不加锁
	public void hello() {
		System.out.println("hello。。。");
	}
}
// 问题3
		public static void ques3() {
			Phone phone = new Phone();// 一个手机资源
			// 创建两个线程

			// 线程A发送邮件
			new Thread(() -> {
				phone.sendEmail();
			}, "A").start();

			// 休眠100毫秒,保证线程A先得到锁
			try {
				TimeUnit.MILLISECONDS.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// 线程B执行普通方法hello
			new Thread(() -> {
				phone.hello();
			}, "B").start();
		}
执行结果

先打印hello,大约隔4秒后打印发邮件

hello。。。
发邮件。。。

结果分析

锁只能锁住资源的同步方法,hello是普通方法,所以即使A线程先得到了锁,也不能阻止B线程访问hello方法。因此A线程得到锁后先睡眠4秒钟,期间B线程访问hello,4秒后A线程苏醒,打印发邮件

4.两个Phone资源类,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
}
// 问题4
	public static void ques4() {
		// 两个手机资源
		Phone phone1 = new Phone();
		Phone phone2 = new Phone();
		// 创建两个线程

		// 线程A使用手机1发送邮件
		new Thread(() -> {
			phone1.sendEmail();
		}, "A").start();

		// 休眠100毫秒,保证线程A先得到锁
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 线程B使用手机2发送短信
		new Thread(() -> {
			phone2.sendMessage();
		}, "B").start();
	}
执行结果

先打印发短信,大约隔4秒后打印发邮件

发短信。。。
发邮件。。。

结果分析

两个类就拥有两把不同的对象锁,线程A使用资源1发邮件,不影响线程B使用资源2发短信

5.把两个基本操作改为静态同步方法,同一个Phone资源,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public static synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public static synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
}
// 问题5
	public static void ques5() {
		// 一个手机资源
		Phone phone = new Phone();
		// 创建两个线程

		// 线程A使用手机发送邮件
		new Thread(() -> {
			phone.sendEmail();
		}, "A").start();

		// 休眠100毫秒,保证线程A先得到锁
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 线程B使用手机发送短信
		new Thread(() -> {
			phone.sendMessage();
		}, "B").start();
	}
执行结果

等待大约4秒后显示以下两行结果

发邮件。。。
发短信。。。

结果分析

静态同步方法锁住的是类而不是对象,发短信和发邮件在一个资源类里,共享同一把类锁,线程A先抢占到这把类锁,并执行sleep,不释放锁,等到4秒后继续发邮件,释放锁后线程B才能发短信

6.两个基本操作为静态同步方法,两个Phone资源,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public static synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public static synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}

}
//问题6
public static void ques6() {
		// 两个手机资源
		Phone phone1 = new Phone();
		Phone phone2 = new Phone();
		// 创建两个线程

		// 线程A使用手机1发送邮件
		new Thread(() -> {
			phone1.sendEmail();
		}, "A").start();

		// 休眠100毫秒,保证线程A先得到锁
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		// 线程B使用手机2发送短信
		new Thread(() -> {
			phone2.sendMessage();
		}, "B").start();
	}
执行结果

等待大约4秒后显示以下两行结果

发邮件。。。
发短信。。。

结果分析

类锁与资源对象的多少无关,多个资源对象共享一个类锁

7.发邮件为普通同步方法,发短信为静态同步方法,一个Phone资源,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public static synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}


}
// 问题7
public static void ques7() {
	// 一个手机资源
	Phone phone = new Phone();
	// 创建两个线程

	// 线程A使用手机发送邮件
	new Thread(() -> {
		phone.sendEmail();
	}, "A").start();

	// 休眠100毫秒,保证线程A先得到锁
	try {
		TimeUnit.MILLISECONDS.sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}

	// 线程B使用手机发送短信
	new Thread(() -> {
		phone.sendMessage();
	}, "B").start();
}
执行结果

先打印发短信,大约隔4秒后打印发邮件

发短信。。。
发邮件。。。

结果分析

发邮件方法带的锁为对象锁,发短信带的锁为类锁,两把锁互不干扰

8.发邮件为普通同步方法,发短信为静态同步方法,两个Phone资源,请问是先打印邮件还是短信?
/**
 * 
 * @ClassName: Phone
 * @Description: 手机资源类
 * @author: fuling
 * @date: 2020年9月10日 下午1:29:17
 */
class Phone {
	/**
	 * 
	 * @Title: sendEmail
	 * @Description: 发邮件
	 * @return: void
	 */
	public synchronized void sendEmail() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发邮件。。。");
	}

	/**
	 * 
	 * @Title: sendMessage
	 * @Description: 发短信
	 * @return: void
	 */
	public static synchronized void sendMessage() {
		System.out.println("发短信。。。");
	}
}

// 问题8
	public static void ques8() {
		// 两个手机资源
		Phone phone1 = new Phone();
		Phone phone2 = new Phone();
		// 创建两个线程
		
		// 线程A使用手机1发送邮件
		new Thread(() -> {
			phone1.sendEmail();
				}, "A").start();
			
				// 休眠100毫秒,保证线程A先得到锁
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
			
		// 线程B使用手机2发送短信
		new Thread(() -> {
			phone2.sendMessage();
		}, "B").start();
	}
执行结果

先打印发短信,大约隔4秒后打印发邮件

发短信。。。
发邮件。。。

结果分析

发邮件方法带的锁为对象锁,发短信带的锁为类锁,两把锁互不干扰

总结

解决这类问有主要有两点需要注意
1.sleep只释放cpu时间片,不释放其占用的锁,wait既释放cpu时间片又释放其占用的锁
2.根据锁住的东西不同,可以分为对象锁和类锁,不同类型的锁互不干扰,不是同一把锁也互不干扰

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值