02-线程的两种创建方式继承Thread类和实现Runnable接口

news2024/9/27 12:13:45

继承Thread类创建线程

获取线程对象

编写一个类继承Thread类并在重写的run方法中编写业务逻辑代码,那么这个类就是一个线程类

  • Runnable接口的run方法没有抛出任何异常,所以子类重写run方法时也不能抛出任何异常,对于程序执行中遇到异常时只能捕获不能抛出
// 重写方法抛出的异常范围不能大于被重写方法抛出的异常范围
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

需求: 在主线程中开启一个子线程,让这两个线程每隔一秒都在控制台交替输出,输出60次时结束主线程,输出80次时结束子线程

  • 一个Thread对象能且只能代表一个线程,如果一个Thread对象调用两次start()方法会抛出java.lang.illegalThreadStateException异常
public class Thread01 {
    // main方法是由主线程调用在主栈中运行
    public static void main(String[] args) throws InterruptedException {
        // 创建线程对象
        MyThread cat = new MyThread();
        // 此时表示在主线程中启动了一个子线程,最终会执行线程对象的run方法
        t.start();
		// 线程每隔1秒在控制台输出
        for(int i = 0; i < 60; i++) {
            System.out.println("主线程 i=" + i);
            //让主线程休眠
            Thread.sleep(1000);

        }
    }

// 定义一个线程类
class MyThread extends Thread {
    int times = 0;
    // 重写run方法写上自己的业务逻辑,run方法由MyThread线程调用并在分支栈中运行
    @Override
    public void run() {
        while (true) {
            // 线程每隔1秒在控制台输出
            System.out.println("子线程 times=" + (++times));
            // 让当前线程休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 当times到80退出当前线程
            if(times == 80) {
                break;
            }
        }
    }
}

run方法和start()方法的区别

在主线程中直接通过线程对象调用run方法并不会真正的启动一个线程,程序会先把run方法执行完后才会向下执行,因为此时程序还是只有一个主线程
在这里插入图片描述

通过线程对象调用start()方法会启动一个分支线程即在JVM中开辟一个新的栈空间,只要新的栈空间开出来start()方法就瞬间结束了此时线程也就启动成功了

  • 启动成功的线程会自动调用run方法即入口方法,并且run方法在分支栈的栈底部,执行一个线程最终就是执行该线程run()方法中的代码
  • run和main是平级的: run方法是子线程的入口方法在分支栈的栈底部, main方法是主线程的入口方法在主栈的栈底部

在这里插入图片描述

因为想抢占CPU的线程很多,只有抢到线程的CPU才会被执行,所以线程的可运行状态又分为就绪状态和运行状态

  • 线程的就绪状态: 线程具备了抢占CPU的资格但还没有被执行

  • 线程的运行状态: 线程抢占到了CPU可以被执行

通过线程对象调用start()方法后并不表示对应的线程就一定会立即得到执行,只是将线程状态变成了可运行状态中的就绪状态,底层还会调用start0()方法

在这里插入图片描述

public synchronized void start() {
    //....
    // 真正实现多线程的方法,由JVM调用,底层是c/c++实现,在这个实现的过程中调用了线程对象run方法
    start0();
}

// start0会调用线程对象的run方法
private native void start0();

实现Runnable接口创建线程

由于Java是单继承的,如果一个类已经继承了某个父类,就无法通过继承Thread类的方式创建线程,此时需要通过实现java.lang.Runnable接口并实现run方法

模拟线程代理类的功能实现

Runnable接口只有一个run方法并没有start方法,当我们执行线程对象的run方法时,是通过执行线程代理对象的run方法然后间接调用要目标线程对象的run方法

  • 代理模式: 线程代理类和目标线程类都实现了Runnable接口,线程代理对象一定内聚了要代理的目标线程对象

第一步: 创建需要代理的目标类且实现了Runnable接口

class Tiger extends Animal implements Runnable {
    @Override
    public void run() {
        System.out.println("老虎嗷嗷叫....");
    }
}

第二步: 模拟Thread类创建一个线程代理类也实现了Runnable接口

public class ThreadProxy implements Runnable {
    // 内聚代理的目标对象,类型是Runnable类型
    private Runnable target = null;
    public ThreadProxy(Runnable target) {
         // 接收需要代理的目标对象
        this.target = target;
    }
    @Override
    public void run() {
        if (target != null) {
            // 动态绑定,对象的运行类型还是目标对象的类型
            target.run();
        }
    }
    public void start() {
        // 这个方法是真正实现多线程方法
        start0();
    }
    public void start0() {
        run();
    }
}

第三步: 获取目标对象的线程代理对象: 线程代理对象的start方法---->start0方法---->线程代理对象的run方法---->目标线程对象的run方法

public class Thread02 {
    public static void main(String[] args) {
        Tiger tiger = new Tiger();
        // 调用线程代理对象的start方法,最终调用目标线程对象的run方法
        ThreadProxy threadProxy = new ThreadProxy(tiger);
        threadProxy.start();
    }
}

获取线程代理对象

需求: 完成程序每隔一秒在控制台输出hi,当输出10次后自动退出

public class Thread02 {
    public static void main(String[] args) {
        // 创建一个可运行的普通对象
        MyRunnable r = new MyRunnable();
        // 为可运行的对象生成一个线程代理对象,含有start方法
        Thread thread = new Thread(r); 
        // 调用线程代理对象的start方法,最终调用目标线程对象的run方法
        thread.start();
    }
}
// 创建一个可运行的类还不是线程类
class MyRunnable implements Runnable { 
    int count = 0;
    @Override
    public void run() { //普通方法
        while (true) {
            System.out.println("hi" + (++count) + Thread.currentThread().getName());
            //休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                break;
            }
        }
    }
}

采用匿名内部类的方式创建Runnable接口的实现类对象并为其创建代理对象

public class ThreadTest04 {
    public static void main(String[] args) {
        // 获取线程的代理对象
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                for(int i = 0; i < 100; i++){
                    System.out.println("t线程---> " + i);
                }
            }
        });
        // 启动线程
        t.start();
        for(int i = 0; i < 100; i++){
            System.out.println("main线程---> " + i);
        }
    }
}

在主线程中启动多个子线程一个Thread对象能且只能代表一个线程,如果一个Thread对象调用两次start()方法会抛出illegalThreadStateException异常

public class Thread03 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();//启动第1个线程
        thread2.start();//启动第2个线程
        //...
    }
}

class T1 implements Runnable {
    int count = 0;
    @Override
    public void run() {
        while (true) {
            // 每隔1秒输出 “hello,world”,输出10次
            System.out.println("hello,world " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count == 60) {
                break;
            }
        }
    }
}
class T2 implements Runnable {
    int count = 0;
    @Override
    public void run() {
        // 每隔1秒输出 “hi”,输出5次
        while (true) {
            System.out.println("hi " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count == 50) {
                break;
            }
        }
    }
}

实现Callable接口(8新特性)

获取线程对象

因为Runnable接口和Thread类中的run方法的返回值都是void,所以线程执行完任务之后是无法获取线程返回值的

如果系统委派的线程执行任务时会返回一个结果,如果想要拿到线程的执行结果需要实现Callable接口的call方法(有返回值)类似于run方法

  • 缺点: 在获取线程执行结果的时候,当前线程受阻塞降低了执行效率
方法名功能
public FutureTask(Callable接口实现类对象)创建一个可运行的未来任务类对象,指定线程执行方法的业务逻辑代码
Object get()获取线程的返回结果,执行该方法时会导致当前线程阻塞
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; // JUC包下的java的并发包(新特性)
public class ThreadTest15 {
    public static void main(String[] args) throws Exception {
        // 创建一个可运行的未来任务类对象
        FutureTask task = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception { 
                // 线程执行一个任务,执行之后可能会有一个执行结果
                System.out.println("call method begin");
                Thread.sleep(1000 * 10);
                System.out.println("call method end!");
                int a = 100;
                int b = 200;
                // 自动装箱(300结果变成Integer)
                return a + b; 
            }
        });
        // 为task对象创建线程代理对象
        Thread t = new Thread(task);
        // 启动线程
        t.start();
        // 执行task对象的get方法获取t线程的返回结果,但会导致当前线程阻塞
        Object obj = task.get();
        System.out.println("线程执行结果:" + obj);
        // get方法是为了拿t线程的执行结果,所以需要等t线程执行结束后,主线程才能继续执行
        System.out.println("hello world!");
    }
}

继承类和实现接口的区别

区别与应用

通过继承Thread类或实现Runnable接口这两种方式来创建线程本质上没有区别,因为Thread类本身就实现了Runnable接口

继承Thread类的线程类最终执行的不是同一个线程对象的run方法,如果需要共享变量要将其声明为静态的

  • 执行流程: 调用Thread类的start方法---->调用Thread类的start0方法(启动线程对象)---->调用线程对象的run方法
SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket01
sellTicket01.start();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket02
sellTicket02.start();
//创建售票线程对象,最终调用线程类中run方法的对象是sellTicket03
sellTicket03.start();

实现Runnable接口的线程类最终可以执行同一个线程对象的run方法,如果需要共享对象不需要将其声明为静态的,因为就一个对象可以实现多个线程共享一个资源

  • 将线程对象传给Thread---->调用Thread类的start方法---->调用Thread类的start0方法(启动线程对象)---->调用Thread类的的run方法---->调用线程对象的run方法
SellTicket02 sellTicket02 = new SellTicket02();
// 创建不同的线程代理对象,最终调用线程类中run方法的对象都是sellTicket02
new Thread(sellTicket02).start();//第1个线程-窗口
new Thread(sellTicket02).start();//第2个线程-窗口
new Thread(sellTicket02).start();//第3个线程-窗口

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1310162.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教你一招,用AI免费一分钟生成3D海报!

近些年&#xff0c;人工智能&#xff08;AI&#xff09;凭借其深远的影响和技术创新&#xff0c;引发了各领域的大变革&#xff0c;揭开了设计创作新时代的序幕。设计行业是这场变革的主要受益者之一。以海报设计为例&#xff0c;它过去一直需要专业设计师的精心构思和设计&…

工厂如何通过设备健康管理系统提高设备可靠性

在现代工厂运营中&#xff0c;设备可靠性对于保持生产效率和降低成本至关重要。然而&#xff0c;许多工厂面临设备故障和停机时间的挑战&#xff0c;这对生产计划和盈利能力产生了负面影响。为了解决这一问题&#xff0c;越来越多的工厂正在采用设备健康管理系统来提高设备的可…

鸿鹄云商:Java商城引领商业模式的潮流,免费搭建多种商城模式

java SpringCloud版本b2b2c鸿鹄云商平台全套解决方案 使用技术&#xff1a; Spring CloudSpring BootMybatis微服务服务监控可视化运营 B2B2C平台&#xff1a; 平台管理端(包含自营) 商家平台端(多商户入驻) PC买家端、手机wap/公众号买家端 微服务&#xff08;30个通用…

文心一言 VS 讯飞星火 VS chatgpt (156)-- 算法导论12.3 3题

三、用go语言&#xff0c;对于给定的 n 个数的集合&#xff0c;可以通过先构造包含这些数据的一棵二叉搜索树(反复使用TREE-INSERT 逐个插入这些数)&#xff0c;然后按中序遍历输出这些数的方法&#xff0c;来对它们排序。这个排序算法的最坏情况运行时间和最好情况运行时间各是…

带你手把手解读firejail沙盒源码(0.9.72版本)(三) etc-cleanup

文章目录 main.c代码解释 Makefile代码解释 ├── etc-cleanup │ ├── Makefile │ └── main.c这个文件夹在 linux 环境下使用 git clone 到本地才有&#xff0c;直接下载源代码没有 git clone https://github.com/netblue30/firejail.gitetc-clean 文件夹通常不是一…

Spring Boot+FreeMarker=打造高效Web应用

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring BootFreeMarker的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一. FreeMarker是什么 二…

C#教程(一):面向对象

1、介绍 C#是一种多范式编程语言&#xff0c;但其中一个主要的编程范式是面向对象编程&#xff08;OOP&#xff09;。面向对象编程有一些特点&#xff0c;而C#提供了丰富的功能来支持这些特点。 2、面向对象特点 封装&#xff08;Encapsulation&#xff09;&#xff1a; 封装…

2024年金科《数字媒体技术》专业参考书目及考试大纲

《计算机网络基础》考试大纲 一、参考书目&#xff1a; 《计算机网络技术与应用&#xff08;第 2 版&#xff09;》&#xff0c;段标、张玲主编&#xff0c;电子工业出版社. 二、考试形式&#xff1a; 闭卷&#xff0c;考试时间 90 分钟 三、考试内容和要求 &#xff08;一…

【爬虫课堂】如何高效使用短效代理IP进行网络爬虫

目录 一、前言 二、代理IP的基本知识 三、短效代理IP的优势 四、高效使用短效代理IP的技巧 1. 多源获取代理IP 2. 质量筛选代理IP 3. 使用代理池 4. 定时更换代理IP 5. 失败重试机制 6. 监控和自动化 五、示例代码 六、结语 一、前言 网络爬虫是一种自动化程序&am…

RocketMQ源码 Broker-BrokerStatsManager Broker统计管理组件源码分析

前言 BrokerStatsManager 主要负责对broker端的系统指标进行统计&#xff0c;如QUEUE_GET_NUMS队列获取数量、QUEUE_GET_SIZE队列获取大小指标的 分钟、小时、天级别的统计数据。它针对的所有指标都是使用后台定时调度线程&#xff0c;对统计条目中的数据进行后台统计计算&…

Leetcode—108.将有序数组转换为二叉搜索树【简单】

2023每日刷题&#xff08;五十八&#xff09; Leetcode—108.将有序数组转换为二叉搜索树 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ struct TreeNode* dfs(int …

Halcon 模板匹配基于相关性

文章目录 基于相关性使用匹配助手 基于相关性 适用场景 模板匹配&#xff1a;当你需要在图像中找到一个已知的模板时&#xff0c;例如在工业生产线上检测产品的特定标识或零件的特征时&#xff0c;相关性匹配是一种简单而有效的方法。实时应用&#xff1a;相关性匹配通常具有较…

scratch认识图形 2023年12月中国电子学会 图形化编程 scratch编程等级考试二级真题和答案解析

目录 scratch认识图形 一、题目要求 1、准备工作 2、功能实现 二、案例分析

(0-1)分布

假设离散型随机变量X只可能取到0、1两个值&#xff0c;它的分布律为&#xff1a; &#xff0c;其中&#xff0c; 那么称X服从参数为p的0-1分布&#xff0c;也叫两点分布。 其实上面公式就是将下面两个式子写在一起&#xff1a;

【RTOS学习】任务创建 | 任务启动 | 任务切换 | 任务暂停和恢复 | 任务阻塞和唤醒 | 临界资源保护

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f30f;任务创建&#x1f9ed;TCB和栈&#x1f9ed;伪造现场&#x1f9ed;链表操作 &am…

solidity 特性导致的漏洞

目录 1、默认可见性 2、浮点数精度缺失 3、错误的构造函数 4、自毁函数 5、未初始化指针-状态变量覆盖 1、默认可见性 Solidity 的函数和状态变量有四种可见性&#xff1a;external、public、internal、private。函数可见性默认为 public&#xff0c;状态变量可见性默认为…

51单片机控制1602LCD显示屏输出自定义字符二

51单片机控制1602LCD显示屏输出自定义字符二 1.概述 1602LCD除了内置的字符外还提供自定义字符功能&#xff0c;当内置的字符中没有我们想要输出的字符时&#xff0c;我们就可以自己创造字符让他显示&#xff0c;下面介绍1602如何创建自定义字符。 2.1602LCD创建字符原理 自…

2023 re:Invent使用 PartyRock 和 Amazon Bedrock 安全高效构建 AI 应用程序

前言 本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 “Your Data, Your AI, Your Future.&#xff08;你的数据&#xff0c;你的AI&…

利用Microsoft Visual Studio Installer Projects打包安装包

利用Microsoft Visual Studio Installer Projects打包安装包 具体步骤步骤1&#xff1a;安装扩展步骤2&#xff1a;创建 Setup 项目步骤3&#xff1a;设置属性步骤4&#xff1a;添加输出步骤5&#xff1a;添加文件步骤6&#xff1a;添加桌面快捷方式步骤7&#xff1a;添加菜单快…

使用Pytorch从零开始构建StyleGAN

本文介绍的是当今最好的 GAN 之一&#xff0c;来自论文《A Style-Based Generator Architecture for Generative Adversarial Networks》的 StyleGAN &#xff0c;我们将使用 PyTorch 对其进行干净、简单且可读的实现&#xff0c;并尝试尽可能接近原始论文。 如果您没有阅读过…