多线程

2 线程创建

【续】2.2 龟兔赛跑案例

  1. 首先需要一个赛道距离,然后会距离终点越来越近
  2. 判断比赛是否结束
  3. 打印出胜利者
  4. 龟兔赛跑开始
  5. 故事中是乌龟获胜,兔子需要睡觉,所以要模拟兔子睡觉
  6. 最终,乌龟赢得比赛
package com.duo.demo01;

public class Race implements Runnable {

    private static String Winner;  //胜利者

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {

            boolean flag = gameOver(i);
            if (flag) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
        }
    }

    //判断是否完成比赛
    private boolean gameOver(int steps) {
        if (Winner != null) {
            return true;
        } {
            if (steps >= 100) {
                Winner = Thread.currentThread().getName();
                System.out.println("Winner is " + Winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race, "乌龟").start();
        new Thread(race, "兔子").start();
    }
}

运行结果:

【多线程】-- 03 龟兔赛跑案例&amp;线程创建方法之三:Callable接口-LMLPHP

可以发现,在未对兔子设置”睡觉“代码体时,每次运行程序的结果符合前节所述,即线程开启后不一定立即执行,由CPU调度执行。添加如下代码:

			if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

运行结果:

【多线程】-- 03 龟兔赛跑案例&amp;线程创建方法之三:Callable接口-LMLPHP

此后,每次运行可以实现获胜者总是乌龟,而兔子最终每次只跑了不到十步。

2.3 实现Callable接口

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool();
  5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

可利用Callable改造之前的下载图片案例:

package com.duo.demo02;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//线程创建方式三:实现Callable接口
public class CallableTest implements Callable<Boolean> {
    private final String url;  //网络图片地址
    private final String name;  //下载保存的图片文件名

    public CallableTest(String url, String name) {
        //构造器传参
        this.url = url;
        this.name = name;
    }

    //下载图片线程的执行体
    @Override
    public Boolean call() {
        webDownLoader webDownLoader = new webDownLoader();
        webDownLoader.downLoader(url, name);
        System.out.println(name + "已下载完成");

        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest t1 = new CallableTest("https://img-blog.csdnimg.cn/a4bbb67340ce46a293b725868b3997b4.jpeg", "星空.jpg");
        CallableTest t2 = new CallableTest("https://img-blog.csdnimg.cn/8dc90f70dca8437d868c655bcd0db7fc.jpeg", "黄昏.jpg");
        CallableTest t3 = new CallableTest("https://img-blog.csdnimg.cn/cc83486a8a7b46a193dac95decc4ad31.jpeg", "旷野.jpg");

        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> result1 = ser.submit(t1);
        Future<Boolean> result2 = ser.submit(t2);
        Future<Boolean> result3 = ser.submit(t3);

        //获取并打印结果
        boolean r1 = result1.get();
        boolean r2 = result2.get();
        boolean r3 = result3.get();
        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);

        //关闭服务
        ser.shutdownNow();
    }
}

//下载器类
class webDownLoader {
    //下载方法
    public void downLoader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            System.out.println("IO异常,downLoader方法出现问题");
        }
    }
}

运行结果:

【多线程】-- 03 龟兔赛跑案例&amp;线程创建方法之三:Callable接口-LMLPHP

可以发现,这种实现方式可以定义返回值。

【小结】

使用实现Callable接口方法的好处:

  • 可以自行定义返回值
  • 可以抛出异常
11-26 16:58