多线程
2 线程创建
【续】2.2 龟兔赛跑案例
- 首先需要一个赛道距离,然后会距离终点越来越近
- 判断比赛是否结束
- 打印出胜利者
- 龟兔赛跑开始
- 故事中是乌龟获胜,兔子需要睡觉,所以要模拟兔子睡觉
- 最终,乌龟赢得比赛
package com.duo.demo01;public class Race implements Runnable {private static String Winner; //胜利者@Overridepublic 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();}
}
运行结果:
可以发现,在未对兔子设置”睡觉“代码体时,每次运行程序的结果符合前节所述,即线程开启后不一定立即执行,由CPU调度执行。添加如下代码:
if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}
运行结果:
此后,每次运行可以实现获胜者总是乌龟,而兔子最终每次只跑了不到十步。
2.3 实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool();
- 提交执行:
Future<Boolean> result1 = ser.submit(t1);
- 获取结果:
boolean r1 = result1.get();
- 关闭服务:
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;}//下载图片线程的执行体@Overridepublic 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方法出现问题");}}
}
运行结果:
可以发现,这种实现方式可以定义返回值。
【小结】
使用实现Callable接口方法的好处:
- 可以自行定义返回值
- 可以抛出异常