(温馨提示:本题最重要的是学习思路,代码还有待优化和改进!)
- 下一篇博客进行优化。实现Callable接口:V call() throws Exception 。可以返回结果,以及可以抛出异常。(启动线程比较麻烦,不能直接传给Thread()参数值)
目录
一、需求。
(1)要求使用多线程模拟龟兔赛跑。
(2)详细描述需要完成的工作。
- 兔子可能因睡觉时间长,乌龟赢得比赛。兔子也可能因睡觉时间短,乌龟输掉比赛。
- 兔子、乌龟各跑100 米。
- 兔子每100 毫秒跑2米,在80米处兔子睡觉: (睡觉时间自定义:如5s) (提示:Thread.sleep())
- 乌龟每100 毫秒跑1米。乌龟不睡觉。(提示:Thread.sleep())
二、分析。
(1)实现100米。
- 用累加的思想。两个线程都是累加到100。
- 谁累加到100后,用的时间较少,谁就赢了。
(2)模拟速度。
- 因为是累加操作。所以每次for循环执行run()方法。
- 兔子的速度。每跑完2米"睡一下",也就是睡100毫秒。(Thread.sleep())
- 乌龟的速度。每跑完1米"睡一下",也就是睡100毫秒。
(3)兔子到达80米时。
- 兔子线程累加达到80时,就指定兔子睡眠时间。
(4)统计时间。
- 定义一个long类型的变量存储时间。
- private私有属性。提供getter()方法。
- 记录开始时间与结束时间。
(5)判断输赢。
- 在测试类中,是main方法中执行代码。
- 也就是要控制我们的main线程执行。它要等"兔子"与"乌龟"线程都执行完毕,主线程(main)才能根据双方用的时间来统计比赛结果。
- join()方法。要用线程对象调用该方法。(Thread对象.join())。
(6)本题实现多线程采用的方法。
- 其中乌龟、兔子实体类都实现java.lang.Runnable接口,重写run()方法。
- 在测试类中各自创建一个Runnable对象。然后再通过创建"乌龟"、"兔子"线程。也就是new Thread(Runnable target)。
- 这里的"Runnable target"是传递创建好的"乌龟"与"兔子"的Runnable对象。然后创建的线程执行start()方法,若它抢到时间片,就执行Thread类的run()方法后。然后让该Runnable对象在内部的run()方法中调用本身的run()方法。
- 最终启动并运行乌龟、兔子线程类。
- 解释例子如下。
- 但是启动线程代码复杂,它依赖Thread类,调用Thread类的start()。
- 但是推荐用,因为线程类可以再去继承其他类,扩展性高。
(7)join()方法。
- 调用该方法时,就是最外面的线程要等该线程执行完毕后,才能执行完成。
- 也就是兔子线程、乌龟线程都执行完成后,最外面的main线程才能计算最后的结果。
- 当mian线程执行到这两行代码时,就会等待。
三、IDEA中实现代码。
(代码中注释有详细解释)
(1)兔子类实现Runnable类。
package com.fs; /** * @Title: RabbitThread * @Author HeYouLong * @Package com.fs * @Date 2024/10/10 下午10:08 * @description: 兔子线程 */ public class RabbitThread implements Runnable{ //记录兔子的总跑步时间 private long time; //判断兔子是否跑完100米 private static boolean flag=false; //私有属性获取 public long getTime() { return time; } @Override public void run() { //计时当前时间 long start_time =System.currentTimeMillis(); for (int i = 0; i <100 ; i+=2) { //兔子的速度:每过100毫秒累加2 if(i==80){ //兔子睡觉5s try { //如果想睡10s,则传参数.sleep(10000) System.out.println("兔子睡着了5秒..."); Thread.sleep(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } } try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("兔子跑完"+i+"m"); if(i==98){ flag=true; } } if(flag){ System.out.println("兔子跑完100m"); } time=System.currentTimeMillis()-start_time; } }
(2)乌龟类实现Runnable类。
package com.fs; /** * @Title: TortoiseThread * @Author HeYouLong * @Package com.fs * @Date 2024/10/10 下午10:08 * @description: 乌龟线程 */ public class TortoiseThread implements Runnable { //记录乌龟的总跑步时间 private long time; //判断乌龟是否跑完100米 private static boolean flag=false; //私有属性获取 public long getTime() { return time; } @Override public void run() { //计时当前时间 long start_time =System.currentTimeMillis(); for (int i = 0; i <100 ; i++) { //兔子的速度:每过100毫秒累加1 try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("乌龟跑完"+i+"m"); if(i==99){ flag=true; } } if(flag){ System.out.println("乌龟跑完100m"); } time=System.currentTimeMillis()-start_time; } }
(3)测试类Test。
package com.fs; /** * @Title: Test * @Author HeYouLong * @Package com.fs * @Date 2024/10/10 下午5:34 * @description: */ public class Test { public static void main(String[] args)throws InterruptedException{ System.out.println("龟兔赛跑开始......"); //分别创建实现Runnable接口实例对象 //兔子 RabbitThread rabbitThread = new RabbitThread(); //乌龟 TortoiseThread tortoiseThread = new TortoiseThread(); /* 这个多线程的实现依赖Thread类 具体的原理可以看博客了解,这是实现多线程的一种方法之一 调用方法new Thread(Runnable target) 通过创建的两个线程的start()方法去抢占cpu时间片,如果抢到,执行两个线程中的run()方法 两个线程的run()方法中分别传入Runnable接口的实例(兔子、乌龟) */ //创建乌龟、兔子线程 //参数的值——>传入实现Runnable接口的实例对象 Thread rabbit = new Thread(rabbitThread); Thread tortoise = new Thread(tortoiseThread); //抢时间片(start()方法) rabbit.start(); tortoise.start(); //主线程(main)等待兔子线程、乌龟线程执行完毕 //Thread对象.join()。main方法已经向上抛出异常。 rabbit.join(); tortoise.join(); //兔子、乌龟线程执行完毕后,再开始统计比赛结果 if(rabbitThread.getTime()<tortoiseThread.getTime()){ System.out.println("兔子赢得了比赛!"); }else if(tortoiseThread.getTime()<rabbitThread.getTime()){ System.out.println("乌龟赢得了比赛!"); }else { System.out.println("平局..."); } } }
四、测试结果
(1)让兔子跑到80米时,睡觉5s。
(兔子赢)
(2)让兔子跑到80米时,睡觉10s。
(乌龟赢)