多线程练习:龟兔赛跑

304 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

首先是需求:

  1. 用两个线程分别模拟乌龟和兔子奔跑。兔子和乌龟的速度不同
  2. 两个线程共同跑完总长1000米。
  3. 同一时间只能有一个在跑,另一个处于休息状态。(兔子和乌龟严格轮流交替跑
  4. 当总长度跑完以后,两个线程结束。
  5. 当两个线程都结束后,打印"到达终点"。
  6. 分别输出乌龟和兔子奔跑的距离。

分析:

1.首先需要确定使用什么方式创建线程,这里我们选用实现Runnable接口方式进行创建线程,因为这种方式的好处很多。具体的可以参考这篇文章,可以让你对创建线程的三种方式有一定认识。也有助于了解这次代码的写法。

Evader1997:创建线程的三种方式及Thread源码解析5 赞同 · 4 评论文章

2.读完需求后,我们知道必须有三个变量:一个用于存储总距离1000米,一个用于存储乌龟跑的距离,一个用于存储兔子跑的距离。

3.是两个线程在抢一个资源,并且是严格的交替跑,所以我的想法是将公共资源1000米单独定义在一个Race类中,通过在Rannable实现类中创建构造方法,将Race类对象当做构造方法的参数。在运行类中,创建方法时传入同一个Race对象,这样就实现了两个线程争抢同一个资源。

4.强制交替跑,这里用到了notify方法跟wait方法。

专栏定位是给初学者一些练手的案例,希望已经有经验的读者轻喷,觉得案例简单的可以看看上面这篇文章。

一共包括四个类,一个乌龟类,一个兔子类,一个赛跑(race类),一个运行类。

Race类:该类作用是提供公共资源1000米,提供pao方法用于表示兔子,乌龟跑的距离。

public class Race {
        // 定义总距离1000米
	private int juli = 1000;
        // 定义变量存储乌龟跑的距离
	private int guiju;
        // 定义变量存储兔子跑的距离
	private int tuju;

	public synchronized void pao() {
		if (juli > 0) {
			if (Thread.currentThread().getName().equals("乌龟")) {
				try {
					juli -= 5;
					guiju += 5;
					System.out.println("乌龟跑了5米,总路程还剩" + juli + "米");
                                        // 这里需要注意先唤醒在等待
					notifyAll();
					wait();
					if (juli <= 0) {
						System.out.println("接力完成!兔子跑了" + tuju + "米,乌龟跑了" + guiju + "米");
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else if (Thread.currentThread().getName().equals("兔子")) {
				try {
					if (juli < 10) {
						juli -= 5;
						tuju += 5;
                                                // 这边运行发现结果出现剩余-5米,所以才有了这行判断
						System.out.println("兔子跑了剩下的5米,总路程还剩" + juli + "米");
						notifyAll();
						wait();
					} else {
						juli -= 10;
						tuju += 10;
						System.out.println("兔子跑了10米,总路程还剩" + juli + "米");
						notifyAll();
						wait();
					}
					if (juli <= 0) {
						System.out.println("接力完成!兔子跑了" + tuju + "米,乌龟跑了" + guiju + "米");
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
	}

}

乌龟类:该类的作用是提供一个带参构造,参数是Race类型的参数,这样是为了实现了两个线程争抢一个资源的需求。另外受Thread源码的启发,在乌龟类中我的run方法是不做事的,做事的是Race类中pao()方法。

public class WuGui implements Runnable {
	private Race race;

	public WuGui(Race race) {
		this.race = race;
	}

	@Override
	public void run() {
		while (true) {
			race.pao();
		}
	}

}

兔子类:兔子类跟乌龟类是一样的,同样将创建对象时传入了一个Race类型的对象。

public class TuZi implements Runnable {
	private Race race;

	public TuZi(Race race) {
		this.race = race;
	}

	@Override
	public void run() {
		while (true) {
			race.pao();
		}
	}
}

运行主类:该类的作用是创建一个公共资源对象race,创建龟兔两个线程都传入race对象,让两个线程“跑的是统一段路”

public class RaceDemo {
	public static void main(String[] args) throws Exception {
		Race race = new Race();
		WuGui wg = new WuGui(race);
		TuZi tz = new TuZi(race);
		Thread w = new Thread(wg, "乌龟");
		Thread t = new Thread(tz, "兔子");
		w.start();
		t.start();
	}

}

运行结果:如果你运行后的结果是这,恭喜你完成了这个练习,撒个花。