1. 继承java.lang.Thread

package com.childers.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
* 多线程下载多个图片
* 1. 创建类继承Thread,并重写run方法;
* 2. 在Download类下创建downloadImg方法;
* 3. 使用commons-io中的FileUtils.copyUrlToFile(Url,File)方法,根据参数做对应的下载操作;
* 4. 在run方法中调用downloadImg();
* 5. 在main方法中,new出Test02,通过有参构造传入对应的参数(url,fileName);
* 6. 执行start()方法;
* */
public class Test02 extends Thread{
	//定义私有变量及构造函数,方便main方法中传参
    private String fileName;
    private String url;
    public Test02(String fileName,String url){
        this.fileName = fileName;
        this.url = url;
    }


    @Override
    public void run() {
        Download download = new Download();
        download.downloadImg(fileName,url);
        System.out.println("下载了文件名为"+fileName);
    }

    public static void main(String[] args) {

        Test02 dl1 = new Test02("第一个.jpg", "https://pic.downk.cc/item/5fca0303394ac523782d2256.jpg");
        Test02 dl2 = new Test02("第二个.jpg", "https://pic.downk.cc/item/5fca0303394ac523782d2259.jpg");
        Test02 dl3 = new Test02("第三个.jpg", "https://pic.downk.cc/item/5fca0303394ac523782d225e.jpg");
        dl1.start();
        dl2.start();
        dl3.start();

    }
}

class Download{
    public void downloadImg(String fileName,String url){

        try {
            //commons-io
            FileUtils.copyURLToFile(new URL(url),new File(fileName));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloadImg方法有问题");
        }

    }
}

2. 实现Runnable接口(代理方式)

/*
 * 多线程下载多个图片
 * 1. 创建类实现Runnable接口,并重写run方法;
 * 2. 在Download类下创建downloadImg方法;
 * 3. 使用commons-io中的FileUtils.copyUrlToFile(Url,File)方法,根据参数做对应的下载操作;
 * 4. 在run方法中调用downloadImg();
 * 5. 在main方法中,new出Test02,通过有参构造传入对应的参数(url,fileName);
 * 6. new出Thread,传入Runnable类型的参数(实现了Runnable接口的本测试类)
 * 7. 调用Thread的start()方法
 * */
public class Test03 implements Runnable{

    private String url;
    private String fileName;

    public Test03(String url, String fileName) {
        this.url = url;
        this.fileName = fileName;
    }

    @Override
    public void run() {
        DownLoad downLoad = new DownLoad();
        downLoad.downloadImg(url,fileName);
        System.out.println("下载的是"+fileName);
    }

    public static void main(String[] args) {
        Test03 t01 = new Test03("https://pic.downk.cc/item/5fca0303394ac523782d2256.jpg","第一个.jpg");
        Test03 t02 = new Test03("https://pic.downk.cc/item/5fca0303394ac523782d2259.jpg","第二个.jpg");
        Test03 t03 = new Test03("https://pic.downk.cc/item/5fca0303394ac523782d225e.jpg","第三个.jpg");

        new Thread(t01).start();
        new Thread(t02).start();
        new Thread(t03).start();


    }
}

3. 拓展:实现Callable

/*
 * 实现过程:
 * 1. 实现Callable接口,需要泛型,泛型就是线程执行完后的返回值类型(call方法的返回值类型);
 * 2. Executors.newFixedThreadPool(2)创建一个固定线程池,线程有两个;
 * 3. Executors.newFixedThreadPool(2).submit(Callable)执行线程;
 * 4. 通过上面的返回值.get()获取线程的返回值;
 * 5. 关闭线程池:.shutdown()
 * */
public class TestCallable implements Callable<Person> {
    //是否存在Winner
    private static boolean hasWinner;

    @Override
    public Person call() throws Exception {
        //for循环给线程执行计数
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
            int race = race(i);
            if (race == 1) {//返回值为1,当前线程赢了
                //返回Person对象
                return new Person(Thread.currentThread().getName(), true);
            } else if (race == -1) {//返回值为-1,当前线程输了,返回对象
                return new Person(Thread.currentThread().getName(), false);
            }
        }
        return null;
    }

    public int race(int step) {
        if (hasWinner) {
            return -1;//已经存在winner,当前线程输了,返回-1
        } else if (step >= 100) {
            hasWinner = true;
            return 1;//当前线程赢了,返回1
        }
        return 0;//没有人胜出,返回0
    }

    public static void main(String[] args) {
        TestCallable testCallable = new TestCallable();
        //创建固定的线程池,大小为2
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //执行线程
        Future<Person> submit1 = executorService.submit(testCallable);
        Future<Person> submit2 = executorService.submit(testCallable);
        try {
            //获取线程的返回值
            Person person1 = submit1.get();
            Person person2 = submit2.get();
            //关闭线程池
            executorService.shutdown();
            //哪个Person对象赢了,就打印该对象赢了,另外一个输了
            if (person1.getWin()) {
                System.out.println(person1.getName() + "赢了!");
                System.out.println(person2.getName() + "输了!");
            } else {
                System.out.println(person2.getName() + "赢了!");
                System.out.println(person1.getName() + "输了!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

前两种方式的优劣

推荐使用第二种(实现Runnable接口);

首先,第一种方式是继承,就受单继承规则的局限性;

那么第二种方式,不受单继承的局限,方便多线程对同一个对象的使用。

巩固:模拟龟兔赛跑:

/*
* 龟兔赛跑情景模拟:
* 1. 创建线程
* 2. 在run()方法中定义了for循环,模拟参赛者的步数(线程执行的计数);
* 3. 默认两个线程都会各自执行100次;
* 4. 根据分析,只要有一个线程的执行次数达到了100次以上,就终止全部线程;
* 5. 在race()方法中,进行了对winner的判断;
* 6. 如果值为空,说明没有胜利者(没有线程执行够100次);则返回false,线程继续执行;
* 7. 如果值为空,且当前线程的执行次数到达了100次,则给winner赋值为当前线程的name,并返回true,终止本线程;
* 8. 当另一个线程执行过来后,发现值不为空,说明已经有别的线程访问够了100次,则返回true终止本线程;
* */

public class Race implements Runnable {

    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
            boolean race = race(i);
            if (race) {
                break;
            }
        }
    }

    public boolean race(int step) {
        if (winner != null) {
            return true;
        }else if (step >= 100) {
            winner = Thread.currentThread().getName();
            System.out.println(winner + "赢了!");
            return true;
        }
        return false;
    }

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