Java多线程:线程创建

线程创建

线程有三种创建方法:继承Thread类,实现Runnable接口,实现Callable类

继承Thread类

  1. 线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动

线程的交替执行

线程开启不一定立即执行,由CPU调度执行

//创建方式1
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("看代码" + i);
        }
    }

    public static void main(String[] args) {
        //main线程,主线程

        //创建线程对象
        TestThread1 testThread1 = new TestThread1();

        //调用start方法开启线程,方法中的代码会交替执行
        testThread1.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("睡觉" + i);
        }
    }
}

输出结果交替执行(模拟多线程,只是切换执行)

看代码0
睡觉0
看代码1
睡觉1
睡觉2
看代码2
看代码3
睡觉3
看代码4
看代码5
看代码6
睡觉4
看代码7
睡觉5
看代码8
睡觉6
看代码9
睡觉7
看代码10
睡觉8
看代码11
睡觉9
睡觉10
看代码12
睡觉11
看代码13
睡觉12
看代码14
看代码15
睡觉13
看代码16
睡觉14
看代码17
睡觉15
看代码18
睡觉16
看代码19
睡觉17
睡觉18
睡觉19

使用commons-IO包并行下载图片

下载器类

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

进行图片的下载

//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{

    private String url;//网络图片地址
    private String name;//保存的文件名

    //有参构造,传入参数
    public TestThread2(String url, String name) {
        this.url = url;
        this.name = name;
    }

    //下载图片线程的执行体,调用定义的下载器进行下载
    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了:" + name);
    }

    public static void main(String[] args) {
        TestThread2 test1 = new TestThread2("https://img-blog.csdnimg.cn/20210311153055723.jpg","1.jpg");
        TestThread2 test2 = new TestThread2("https://snailclimb.gitee.io/javaguide/docs/java/basis/images/shallow&deep-copy.png","2.jpg");
        TestThread2 test3 = new TestThread2("https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/%E9%98%9F%E5%88%97.png","3.jpg");

        //下载顺序没有按顺序执行
        test1.start();
        test2.start();
        test3.start();
    }
}

下载顺序:

下载了:1.jpg
下载了:3.jpg
下载了:2.jpg

实现Runnable(推荐)

  1. 实现Runnable接口
  2. 重写run()方法
  3. 执行线程需要丢入Runnable接口实现类
  4. 调用start()方法

实现Runnable接口后还是通过Thread对象开启线程

public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("看代码" + i);
        }
    }
    //main线程,主线程
    public static void main(String[] args) {

        //创建线程对象
        TestThread3 testThread3 = new TestThread3();

        //创建线程对象,通过线程对象开启线程,代理
//        Thread thread = new Thread(testThread3);
//        //调用start方法开启线程,方法中的代码会交替执行
//        thread.start();
        new Thread(testThread3).start();

        for (int i = 0; i < 20; i++) {
            System.out.println("睡觉" + i);
        }
    }
}

Thread和Runnable对比

  • 继承Thread
    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start();
    • 不建议使用:避免OOP单继承的局限性
  • 实现Runnable接口:
    • 实现接口Runnable具有多线程能力
    • 启动线程:new Thread(传入目标对象).start();
    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

多线程操作同一个对象(并发问题)

存在问题:多个线程同时操作一个资源的情况下,线程不安全,数据紊乱

//多线程操作同一个对象
//买火车票

//问题:多个线程同时操作一个资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable{
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if (ticketNums <= 0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获得线程名字
            System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();

        new Thread(testThread4,"A").start();
        new Thread(testThread4,"B").start();
        new Thread(testThread4,"C").start();
    }
}

可能存在抢到同一张票,抢到-1张票的问题:

C拿到了第9张票
A拿到了第10张票
B拿到了第8张票
B拿到了第7张票
A拿到了第7张票
C拿到了第6张票
B拿到了第5张票
C拿到了第4张票
A拿到了第5张票
A拿到了第3张票
B拿到了第3张票
C拿到了第2张票
B拿到了第1张票
C拿到了第0张票
A拿到了第1张票

模拟龟兔赛跑

//模拟龟兔赛跑
public class Race implements Runnable{
    //胜利者只有一个,winner可以是静态的
    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 + "步");

            //模拟兔子休息,先跑再休息
            if (Thread.currentThread().getName().equals("rabbit") && i % 10 == 0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private boolean gameOver(int steps){
        if (winner != null) {
            return true;
        }else{
            if (steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println(winner + ": win");
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"rabbit").start();
        new Thread(race,"tortoise").start();
    }
}

实现Callable接口

  1. 实现Callable接口,需要返回值
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future<Boolean>result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();
public class TestCallable implements Callable<Boolean> {
    private String url;//网络图片地址
    private String name;//保存的文件名

    public TestCallable(String url,String name) {
        this.url = url;
        this.name = name;
    }

    //重写call,需要返回值,调用下载图片的类
    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了:" + name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("https://img-blog.csdnimg.cn/20210311153055723.jpg","1.jpg");
        TestCallable t2 = new TestCallable("https://snailclimb.gitee.io/javaguide/docs/java/basis/images/shallow&deep-copy.png","2.jpg");
        TestCallable t3 = new TestCallable("https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/%E9%98%9F%E5%88%97.png","3.jpg");

        //创建执行服务(3个线程)
        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 = result1.get();
        boolean r3 = result1.get();

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

优点

  • 可以设定返回值
  • 可以抛出异常
posted @ 2022-04-11 16:45  chachan53  阅读(37)  评论(0编辑  收藏  举报