JavaSE进阶 多线程(2)--线程创建

线程创建

三种创建方式

  • Thread class —> 继承Thread类(重点)
  • Runnable接口 —> 实现Runnable接口(重点)
  • Callable接口 —> 实现Callable接口(现阶段了解)

Thread

创建一个新的执行线程有两种方法,一个是将一个类声明为Thread的子类。这个子类应该重写run类的方法,编写线程执行体。创建线程对象,调用start方法启动线程

交替执行,同时执行

package com.Sucker.Demo01;

//创建线程方法一:继承Thread类,重写run()方法,调用start开启线程

//注意:线程开启不一定立即执行,由cpu调度
public class TestThread1 extends Thread {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("k---"+i);
        }
    }

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

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

        //调用start方法开启线程
        testThread1.start();

        for (int i = 0; i < 200; i++) {
            System.out.println("duo---"+i);
        }
    }
}

线程不一定立即执行,CPU安排调度

案例:下载图片

首先下载commons-io-2.6 jar 包,在com层级下新建package–lib,将其复制进lib目录中,再右键lib目录选择add as library

package com.Sucker.Demo01;

import org.apache.commons.io.FileUtils;

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

//联系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 t1 = new TestThread2("https://img-home.csdnimg.cn/images/20210721094624.jpg","20210721094624.jpg");
        TestThread2 t2 = new TestThread2("https://img-home.csdnimg.cn/images/20210721094624.jpg","25.jpg");
        TestThread2 t3 = new TestThread2("https://img-home.csdnimg.cn/images/20210721094624.jpg","26.jpg");

        t1.start();
        t2.start();
        t3.start();
    }


}


//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            //调用jar包里面写好的类的方法,把url地址变成一个文件
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

步骤:

  • 首先使用下载器,通过工具类里的方法
  • 实现一个线程类,继承Thread类
  • 用构造器丢入url和name
  • 重写run方法,里面执行
  • 主线程main方法里面通过构造器创建多个线程并启动

第二种方法是实现Runnable

  • 定义MyRunnable类实现Runnable接口
  • 重写run方法,实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
package com.Sucker.Demo01;

//创建线程方式二:实现Runnable接口,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("k---"+i);
        }
    }

    public static void main(String[] args) {

        //创建一个Runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();

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

        new Thread(testThread3).start();//简写

        for (int i = 0; i < 1000; i++) {
            System.out.println("duo---"+i);
        }
    }
}

小结:

  • 继承Thread
    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用:避免OOP单继承局限性
  • 实现Runnable
    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
Thread.currentThread().getName()这个方法得到当前执行线程的名字

例子:

package com.Sucker.Demo01;

//多个线程同时操作同一个对象
//买火车票的例子

//发现问题:多个线程操作同一个资源情况下,线程不安全,数据紊乱
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();
            }

            //Thread.currentThread().getName()这个方法得到当前执行线程的名字
            System.out.println(Thread.currentThread().getName()+"--->拿到了"+ticketNums--+"票");
        }
    }

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

        new Thread(ticket,"小明").start();//不同线程,可以起名字
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛党").start();

    }

}

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

案例:龟兔赛跑

package com.Sucker.Demo01;

//模拟龟兔赛跑
public class Race implements Runnable {
    
    //胜利者
    private static String winner; //静态常量static保证只有一个winner

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

            //模拟兔子睡觉
            if(Thread.currentThread().getName().equals("兔子") && i%10==0)
            {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //判断比赛是否结束
            boolean flag = gammeOver(i);
            if(flag) break;

            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        }
    }

    //判断是否完成比赛
    private boolean gammeOver(int steps){
        //判断是否有胜利者
        if(winner!=null) return true;
        else if(steps>=100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is "+winner);
            return true;
        }
        else 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();
package com.Sucker.Demo02;

import com.Sucker.Demo01.TestThread2;
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 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("https://img-home.csdnimg.cn/images/20210721094624.jpg","20210721094624.jpg");
        TestCallable t2 = new TestCallable("https://cn.bing.com/images/search?view=detailV2&ccid=QnfHdp2q&id=BDCDE7C79D83FB599405AFD2188093140A3011E5&thid=OIP.QnfHdp2qT_0YJXjq1gmglgHaM1&mediaurl=https%3a%2f%2fuploadfile.bizhizu.cn%2fup%2f03%2fef%2f8f%2f03ef8fc438dbe2beb844b305ec87391e.jpg&exph=1774&expw=1024&q=jk%e7%be%8e%e5%a5%b3%e5%9b%be%e7%89%87&simid=608040684881330440&FORM=IRPRST&ck=851CB37FC70B002B589BA118850F85A4&selectedIndex=0","25.jpg");
        TestCallable t3 = new TestCallable("https://cn.bing.com/images/search?view=detailV2&ccid=ti3Zfhln&id=5D3EE2D7DE03E7400C8014499C2EE8B94A7F167D&thid=OIP.ti3ZfhlntGiUtwqXhdaLUAHaKL&mediaurl=https%3a%2f%2ftse1-mm.cn.bing.net%2fth%2fid%2fR-C.b62dd97e1967b46894b70a9785d68b50%3frik%3dfRZ%252fSrnoLpxJFA%26riu%3dhttp%253a%252f%252fimg.mm4000.com%252ffile%252f8%252f7b%252f260e19734b.jpg%26ehk%3dsZek3h%252fYAHyVd0KkDUGwyYRYOzMUYLFCkbZ4nbXssj4%253d%26risl%3d%26pid%3dImgRaw&exph=1513&expw=1100&q=jk%e7%be%8e%e5%a5%b3%e5%9b%be%e7%89%87&simid=608040800847024524&FORM=IRPRST&ck=3A1E7FE0E1914FF26937FF1227F44B89&selectedIndex=1","26.jpg");

        //创建线程服务:
        ExecutorService ser = Executors.newFixedThreadPool(3);//3个线程

        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();

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

    }

}

//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            //调用jar包里面写好的类的方法,把url地址变成一个文件
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

Callable的好处:

  • 可以定义返回值
  • 可以抛出异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值