多线程wait/notify与sleep/yield/join的通俗理解

  1. 面向对象设计来源于现实世界,软件中的设计均有现实的场景对应,按照人类世界来理解普通的对象与Thread线程,普通对象拥有内存资源,Thread线程拥有cpu时间片即计算资源;在人类世界中,普通对象可以看成是老板,Thread线程可以看成是员工,老板拥有资源,员工拥有时间和计算技能。wait/notify是Object的方法,即老板的方法,synchronized 指的是Thread线程对Object资源的排斥性占有,但资源是老板的,老板说了算,说给谁就给谁,所以wait方法调用的时候是释放锁的。
    以下为示例代码:
package org.loyuru.multi.thread.composite;

/**
 * 公司老板让采购部职员张三采购电脑放到仓库中,让IT部李四从仓库中取电脑里安装操作系统 
 * 因为wait/notify是老板的方法,老板能控制张三和李四,让他们协作工作
 * 老板让他们等待的时候,会把仓库的锁收回来,给另外一个人
 * @author Teddy Lee
 * @date 2020年3月16日
 */
public class Company {
	private int computer = 0;

	
	public synchronized void purchaseComputer() {
		if (this.computer > 4) {
			try {
				System.out.println(Thread.currentThread().getName()+":老四,仓库已经满了,先不采购新电脑了,等你安装完了再采购!");
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		computer++;
		System.out.println(Thread.currentThread().getName()+":采购新电脑来了,放到了仓库里,老四你快来安装操作系统!");
		notify();
	}

	
	public synchronized int installSystem() {
		if (this.computer == 0) {
			try {
				System.out.printf(Thread.currentThread().getName()+":老三,仓库电脑都安装完成操作系统了,我等着你采购!");
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		computer--;
		System.out.println(Thread.currentThread().getName()+":我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!");

		notify();
		return computer;
	}

	public static void main(String[] args) {
		Company boss = new Company();
		Thread zhangsan = new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				boss.purchaseComputer();
			}

		});
		zhangsan.setName("张三");

		Thread lisi = new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				boss.installSystem();
			}
		});
		lisi.setName("李四");

		zhangsan.start();
		lisi.start();

	}

}


执行结果:
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:老四,仓库已经满了,先不采购新电脑了,等你安装完了再采购!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:老三,仓库电脑都安装完成操作系统了,我等着你采购!张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
张三:采购新电脑来了,放到了仓库里,老四你快来安装操作系统!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!
李四:我安装了一个操作系统,仓库有空余位置了,老三你可以采购了!

  1. sleep是Tread类方法,在现实生活中可以理解为员工工作累了,想偷懒休息一会,但员工上班时间不能离开公司啊,所以不会释放锁。
    以下为示例代码:
package org.loyuru.multi.thread.composite;

/**
 * 公司老板让采购部职员张三采购电脑放到仓库中,让IT部李四从仓库中取电脑里安装操作系统
 * sleep是员工的方法,员工偷懒睡觉,但是不敢离开公司,即不敢释放锁
 * 
 * @author Teddy Lee
 * @date 2020年3月16日
 */
public class Company2 {
	private int computer = 0;

	public void purchaseComputer() {
		computer++;
		System.out.println(Thread.currentThread().getName() + ":采购新电脑来了!");
	}

	public int installSystem() {
		computer--;
		System.out.println(Thread.currentThread().getName() + ":我安装了一个操作系统!");
		return computer;
	}

	public static void main(String[] args) {
		Company2 boss = new Company2();
		Thread zhangsan = new Thread(() -> {
			synchronized (boss) {
				for (int i = 0; i < 10; i++) {
					boss.purchaseComputer();
					try {
						System.out.println(Thread.currentThread().getName() + ":我累了,睡会觉!");
						Thread.sleep(100l);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		});
		zhangsan.setName("张三");

		Thread lisi = new Thread(() -> {
			synchronized (boss) {
				for (int i = 0; i < 10; i++) {
					boss.installSystem();
					try {
						System.out.println(Thread.currentThread().getName() + ":我累了,睡会觉!");
						Thread.sleep(100l);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		});
		lisi.setName("李四");

		zhangsan.start();
		lisi.start();

	}

}


执行结果:
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
张三:采购新电脑来了!
张三:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!
李四:我安装了一个操作系统!
李四:我累了,睡会觉!

  1. yield方法是Thread类方法,调用后可能出让cpu时间片,但不会释放锁,不稳定,因为调用后不一定什么时候出让时间片。现实中,员工正在敲代码,然后感觉有点累,想着要不要休息会,休息的过程中,行政部女秘书会来打扫办公桌。
    以下为示例代码:
package org.loyuru.multi.thread.composite;

/**
 * 张三和李四都是程序员,休息的时候,可以叫行政女秘书来打扫办公桌
 * 
 * @author Teddy Lee
 * @date 2020年3月16日
 */
public class Company3 {

	public void code() {
		System.out.println(Thread.currentThread().getName() + ":编码中!");
	}

	public static void main(String[] args) {
		Company3 company = new Company3();
		Thread zhangsan = new Thread(() -> {
			synchronized (company) {
				for (int i = 0; i < 5; i++) {
					company.code();
					System.out.println(Thread.currentThread().getName() + ":我要不要休息一会?");
					Thread.yield();
				}
			}

		});
		zhangsan.setName("张三");
		
		Thread lisi = new Thread(() -> {
			synchronized (company) {
				for (int i = 0; i < 5; i++) {
					company.code();
					System.out.println(Thread.currentThread().getName() + ":我要不要休息一会?");
					Thread.yield();
				}
			}

		});
		lisi.setName("李四");

		Thread xingzhengGirl = new Thread(() -> {
			// Object代表维修工具
			synchronized (new Object()) {
				for (int i = 0; i < 5; i++) {
					System.out.println(Thread.currentThread().getName() + ":打扫办公桌");
					Thread.yield();
				}
			}

		});
		xingzhengGirl.setName("行政女秘书");

		zhangsan.start();
		lisi.start();
		xingzhengGirl.start();

	}

}

执行结果:(yield出让时间片不稳定,可多次尝试查看效果)
张三:编码中!
张三:我要不要休息一会?
张三:编码中!
张三:我要不要休息一会?
张三:编码中!
张三:我要不要休息一会?
张三:编码中!
张三:我要不要休息一会?
张三:编码中!
张三:我要不要休息一会?
行政女秘书:打扫办公桌
行政女秘书:打扫办公桌
行政女秘书:打扫办公桌
李四:编码中!
李四:我要不要休息一会?
李四:编码中!
李四:我要不要休息一会?
李四:编码中!
李四:我要不要休息一会?
李四:编码中!
李四:我要不要休息一会?
李四:编码中!
李四:我要不要休息一会?
行政女秘书:打扫办公桌
行政女秘书:打扫办公桌

  1. join方法是Thread对象方法,底层调用的Object的wait方法,调用后,当前线程等待join线程完成任务后再进行工作,相当于现实中,两位同事需要协作完成工作。
    以下为代码示例:
package org.loyuru.multi.thread.composite;

/**
 * 张三要做PPT,需要李四上网查资料并放到U盘中,给他
 * 
 * @author Teddy Lee
 * @date 2020年3月16日
 */
public class Company4 {
	private int computer = 0;

	public synchronized void ppt() {
		System.out.println(Thread.currentThread().getName() + ":编写PPT完成!");
	}

	public synchronized void material() {
		for (int i = 0; i < 10; i++) {
			switch (i) {
			case 0:
				System.out.println(Thread.currentThread().getName() + ":第" + i + "步,查资料!");
				break;
			case 8:
				System.out.println(Thread.currentThread().getName() + ":第" + i + "步,存到U盘!");
				break;
			case 9:
				System.out.println(Thread.currentThread().getName() + ":第" + i + "步,交资料!");
				break;
			default:
				System.out.println(Thread.currentThread().getName() + ":第" + i + "步,XXX!");

			}
		}
	}

	public static void main(String[] args) {
		Company4 boss = new Company4();

		Thread lisi = new Thread(() -> {
			boss.material();
		});
		lisi.setName("李四");

		Thread zhangsan = new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + ":准备编写PPT!");
			try {
				lisi.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			boss.ppt();

		});
		zhangsan.setName("张三");
		zhangsan.start();
		lisi.start();

	}

}

结果:
张三:准备编写PPT!
李四:第0步,查资料!
李四:第1步,XXX!
李四:第2步,XXX!
李四:第3步,XXX!
李四:第4步,XXX!
李四:第5步,XXX!
李四:第6步,XXX!
李四:第7步,XXX!
李四:第8步,存到U盘!
李四:第9步,交资料!
张三:编写PPT完成!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值