Java篇之多线程-02-线程创建

三种创建方式

在这里插入图片描述

Thread

  • 自定义线程类继承 Thread 类
  • 重写 run() 方法,编写线程执行体
  • 创建线程对象,调用 start() 方法启动线程
package com.moon.demo01;

/*
    创建线程方式:
    1. 继承 Thread 类
    2. 重写 run() 方法
    3. 调用 start() 开启线程
    线程开启不一定立即执行,由 CPU 调度执行
 */
public class TestThread extends Thread {
    // 线程入口点
    @Override
    public void run() {
        // run 方法 线程体
        // 线程不一定立即执行,由 CPU 安排调度
        for (int i = 0; i < 200; i++) {
            System.out.println("多线程--" + i);
        }
    }

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

        // 创建线程对象
        TestThread testThread = new TestThread();
        // 调用 start() 方法开启线程
        testThread.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("main 方法--" + i);
        }
    }
}

网络图片下载练习

package com.moon.demo02;

// 需要引入 commons-io-2.8.0.jar 包
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

// 下载器
public 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 异常,downloader 方法异常");
        }
    }
}
package com.moon.demo02;

// 练习 Thread,实现多线程同步下载图片
public class TestThread extends Thread{
    private String url; // 网络图片地址
    private String name; // 保存的文件名

    public TestThread(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) {
        TestThread t1 = new TestThread("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
        TestThread t2 = new TestThread("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
        TestThread t3 = new TestThread("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.jpg");
        t1.start();
        t2.start();
        t3.start();
        /*
        下载文件02.jpg成功
        下载文件01.jpg成功
        下载文件03.jpg成功
        // 并不一定按照预想的程序顺序执行下载,每次下载的先后顺序也不一样,由 CPU 调度执行
         */
    }
}

Runnable

  • 定义 MyRunnable 类,实现 Runnable 接口
  • 实现 run() 方法,编写线程执行体
  • 创建线程对象, 调用 start() 方法启动线程
  • 推荐使用 Runnable 对象,因为 Java 单继承的局限性
package com.moon.demo03;

/*
    创建线程方式
    1. 实现 runnable 接口
    2. 实现 run 方法
    3. 执行线程需要创建线程对象并丢入 runnable 接口实现类,调用 start() 方法
 */
public class TestThread implements Runnable {
    @Override
    public void run() {
        // 实现 run 方法 线程体
        for (int i = 0; i < 200; i++) {
            System.out.println("多线程--" + i);
        }
    }

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

        // 创建 runnable 接口的实现对象
        TestThread testThread = new TestThread();

        // 创建线程对象,通过线程对象来开启线程 代理
        //Thread thread = new Thread(testThread);
        // 开启线程
        //thread.start();

        // 以上两个步骤可简写为
        new Thread(testThread).start();


        for (int i = 0; i < 2000; i++) {
            System.out.println("main 方法--" + i);
        }
    }
}

以上网络图片下载练习使用 Runnable 实现

package com.moon.demo02;

// 练习 Thread,实现多线程同步下载图片
public class TestThread implements Runnable{
    private String url; // 网络图片地址
    private String name; // 保存的文件名

    public TestThread(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) {
        TestThread t1 = new TestThread("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
        TestThread t2 = new TestThread("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
        TestThread t3 = new TestThread("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.jpg");
        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}

小结:

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

// 多个线程同时操作一个对象
// 模拟买票例子
// 发现问题:多个线程操作同一个资源的情况下,线程不安全、数据紊乱
public class TestThread 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();
            }
            // 获取当前执行的线程名
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "--> 抢到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {
        TestThread ticket = new TestThread();
        
        // 一个对象被多个线程使用
        new Thread(ticket, "小明").start();
        new Thread(ticket, "小红").start();
        new Thread(ticket, "隔壁老王").start();
    }

}

案例:龟兔赛跑-Race

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

// 模式龟兔赛跑
public class Race implements Runnable {
    private static String winner; // 胜利者

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            String threadName = Thread.currentThread().getName();

            // 模拟兔子休息
            if (threadName.equals("兔子") && i % 10 == 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 判断比赛是否结束
            boolean flag = this.gameOver(i);
            if (flag) {
                break;
            }

            System.out.println(threadName + " --> 跑了" + i + "米");
        }
    }

    // 判断是否完成比赛
    private boolean gameOver(int meter) {
        // 已近存在胜利者
        if (winner != null) {
            return true;
        }
        if (meter >= 100) {
            Race.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();
    }
}

Callable

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

利用 callable 改造下载图片案例

package com.moon.demo06;

import java.util.concurrent.*;

// 练习 Thread,实现多线程同步下载图片
// 实现 callable 接口
/*
    callable 优势
    1. 可以定义返回值
    2. 可以抛出异常
 */
public class TestCallable implements Callable<Boolean> {
    private String url; // 网络图片地址
    private String name; // 保存的文件名

    public TestCallable(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 {
        TestCallable t1 = new TestCallable("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
        TestCallable t2 = new TestCallable("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
        TestCallable t3 = new TestCallable("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.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();
    }
}

静态代理

演示:实现静态代理对比 Thread

  • 你:真实角色
  • 婚庆公司:代理你,帮你处理结婚的事
  • 结婚:你和婚庆公司都实现结婚接口即可
package com.moon.demo07;

// 结婚接口
public interface Marry {
    void happyMarry();
}
package com.moon.demo07;

// 真实角色(你):你去结婚
public class You implements Marry{
    @Override
    public void happyMarry() {
        System.out.println("我们结婚啦~~");
    }
}
package com.moon.demo07;

// 代理角色(婚庆公司):帮助你结婚
public class WeddingCompany implements Marry{
    // 代理谁 --> 真实目标角色
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void happyMarry() {
        before();
        this.target.happyMarry(); // 真实对象
        after();
    }

    private void before() {
        System.out.println("结婚之前,布置现场中...");
    }

    private void after() {
        System.out.println("结婚之后,收尾款!!!");
    }
}
package com.moon.demo07;

/*
  静态代理模式:
      真实对象和代理对象都要实现同一个接口
      代理对象要代理真实角色

  好处:
      代理对象可以做很多真实对象做不了的事情
      真实对象可以专注做自己的事情
 */
public class StaticProxy {
    public static void main(String[] args) {
        //new Thread(new ThreadClassName()).start(); 与 Runnable 开启多线程一致
        new WeddingCompany(new You()).happyMarry();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值