JavaWeb3-线程的3种创建方式7种写法

news2024/12/28 19:37:27

目录

1.方式一:继承Thread(2种写法)

写法①(常规):

a.使用jconsole观察线程

b.启动线程——start方法

PS:(常见面试题)start 方法与 run 方法的区别:

写法②(简化):匿名方式创建子对象

2.方式二:实现Runnable接口(3种写法)

写法③(常规):

写法④(变种):匿名Runnable方式(匿名内部类)

写法⑤(变种):使用Lambda匿名Runable方式(更简单)

3.方式三:实现Callable接口(带返回值可为任意类型)(2种写法)

写法⑥(常规):Callable + FutureTask(一个容器,用来接收线程执行的返回值)

写法⑦(简单):匿名Callable


1.方式一:继承Thread(2种写法)

写法①(常规):

/**
 * 继承 Thread 创建线程
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //获得当前的线程
        Thread mainThread = Thread.currentThread();
        System.out.println("主线程的名称:" + mainThread.getName());

        //创建线程
        Thread thread = new MyThread();
        //开启线程
        thread.start();
    }
}

class MyThread extends Thread {
    @Override //需要重写run方法
    public void run() {
        //具体的业务执行代码
        Thread thread = Thread.currentThread(); //得到当前运行的线程
        try {
            Thread.sleep(60 * 60 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程的名称:" + thread.getName()); //打印当前线程的名称
    }
}

a.使用jconsole观察线程

启动上面程序后:

b.启动线程——start方法

  • 通过覆写 run ⽅法创建⼀个线程对象,但线程对象被创建出来并不意味着线程就开始运⾏了。(相当于提供给线程要做的事情的指令清单。 线程对象可以认为是把李四、王五叫过来了)
  • 调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程。 就是喊⼀声:”⾏动起来!“,线程才真正独⽴去执⾏了)

PS:(常见面试题)start 方法与 run 方法的区别:

public class ThreadDemo3 {
    public static void main(String[] args) {
        //创建线程
        Thread thread = new MyThread();
        //开启线程
        thread.start();
//        thread.run();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("你好,线程");
    }
}

二者执行结果都是:

二者具体区别:

区别一:【方法性质不同】run 是一个普通方法,而 start 是开启新线程的方法。

  • 调用 run 方法相当于执行普通方法 run,其实是调用当前主程序 main 来执行方法体的,(所有的main方法都会创建一个名为main的线程)并不会开启新线程;
  • 而调用 start 方法是真正开启一个新线程来执行任务。
public class ThreadDemo3 {
    public static void main(String[] args) {
        //创建线程
        Thread thread = new MyThread();
        //开启线程
        thread.start();
//        thread.run();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}

start:

run:

区别二:【执行速度不同】调用 run 方法会立即执行任务,调用 start 方法是将线程的状态改为就绪状态,不会立即执行。

  • run 方法也叫做线程体,它里面包含了具体要执行的业务代码,当调用 run 方法时,会立即执行 run 方法中的代码(如果当前线程时间片未用完);
  • 而调用 start 方法时,是启动一个线程并将线程的状态设置为就绪状态,并不会立即执行。

区别三:【调用次数不同】run 方法可以被重复调用,而 start 方法只能被调用一次。

  • 因为 run 方法是普通方法,而普通方法是可以被多次调用的,所以 run 方法可以被调用多次;
  • 而start 方法是创建新线程来执行任务,因为线程只能被创建一次,且线程状态是不可逆的。当线程调用了第一个 start 方法之后,线程的状态就会从新建状态 NEW,变为就绪状态 RUNNABLE,此时再次调用 start 方法,JVM 就会判断出当前的线程已经不等于新建状态,从而抛出 IllegalThreadStateException 非法线程状态异常。

start方法源码:

【Thread 在 start 的实现源码中做了判断看当前线程的状态是不是等于 0,也就是是否为新建状态NEW,如果线程不是新建状态 NEW,则会抛出非法线程状态异常 IllegalThreadStateException。】

public class ThreadDemo3 {
    public static void main(String[] args) {
        //创建线程
        Thread thread = new MyThread(); //NEW新建状态
        //开启线程
        thread.run(); //RUNABLE运行状态
        thread.run();
        thread.run();
        thread.start();
        thread.start();
        thread.start();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}

写法②(简化):匿名方式创建子对象

public class ThreadDemo6 {
    public static void main(String[] args) {
        //创建线程并初始化
        Thread thread = new Thread() {
            @Override
            public void run() {
                Thread t = Thread.currentThread();
                System.out.println("任务执行:" + t.getName());
            }
        };
        //执行线程
        thread.start();
    }
}

写法和↓类似:

List<String> list = new ArrayList<String>() {{
    add("ddd");
}};

继承Thread新建线程的缺点:Java 是单继承,继承了 Thread 就不能继承其他类了;然⽽ Java 可以实现多个接⼝,于是有了下⼀种⽅式。

2.方式二:实现Runnable接口(3种写法)

写法③(常规):

/**
 * 实现Runnable接口新建线程
 */
public class ThreadDemo4 {
    public static void main(String[] args) {
        // Runnable不是Thread的子类,所以不能用之前的方式直接new一个MyThread2()
        // 而Thread类可以接收一个Runnable接口作为它的入参
        //创建Runnable
        MyThread2 myThread2 = new MyThread2();
        //创建一个线程
        Thread thread = new Thread(myThread2);
        //启动线程
        thread.start();
    }
}

class MyThread2 implements Runnable {
    @Override
    public void run() {
        //具体的业务代码
        Thread thread = Thread.currentThread(); //得到当前线程
        System.out.println("线程执行:" + thread.getName());
    }
}

写法④(变种):匿名Runnable方式(匿名内部类)

/**
 * Runnable匿名内部类来创建
 */
public class ThreadDemo5 {
    public static void main(String[] args) {
        //匿名内部类
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //业务代码
                Thread t = Thread.currentThread();
                System.out.println("执行任务:" + t.getName());
            }
        });
        //启动线程
        thread.start();
    }
}

写法⑤(变种):使用Lambda匿名Runable方式(更简单)

/**
 * 使用 lambda 来创建 Runnable
 */
public class ThreadDemo7 {
    public static void main(String[] args) {
        //创建线程
        Thread thread = new Thread(() -> {
            //具体业务
            Thread t = Thread.currentThread();
            System.out.println("任务执行:" + t.getName());
        });
        //启动线程
        thread.start();
    }
}

进入Lambda表达式查看源码:

Lambda表达式是对于Runnable这个FunctionalInterface的一个实例化。

注:以上简单&变种支持JDK1.8+版本。

以上5种写法的共同问题:都没有返回值,当线程执行完成之后,主线程没法拿到新线程的执行结果。

3.方式三:实现Callable接口(带返回值可为任意类型)(2种写法)

写法⑥(常规):Callable + FutureTask(一个容器,用来接收线程执行的返回值)

Runnable(JDK1.0)比Callable(JDK1.5)诞生得早,后来当需要拿到线程的返回值时(使用频率低),为了不增加学习成本,改动原有Thread写法,新创建了FutureTask来接收线程执行的返回值。

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 实现Callable新建线程
 */
public class ThreadDemo8 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建Callable实例
        MyCallable myCallable = new MyCallable();
        //用于接收Callable结果的对象
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        //创建新线程
        Thread thread = new Thread(futureTask); //Thread线程只能接收2种类型的参数,要么是Runnable,要么是FutureTask,不能直接接收一个Callable对象
        //启动线程
        thread.start();
        //接收新线程执行的结果
        int result = futureTask.get();
        System.out.println(Thread.currentThread().getName() + "——新线程返回的结果为:" + result);
    }
}

/**
 * Callable<V> 泛型里面可以是任意数据类型
 */
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //生成随机数的范围是0-9
        int randomNum = new Random().nextInt(10);
        System.out.println(Thread.currentThread().getName() + "——随机数:" + randomNum);
        return randomNum;
    }
}

写法⑦(简单):匿名Callable

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo9 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                //新线程执行的业务代码
                String[] arrs = new String[]{"Java", "MySQL","Thread"};
                //随机返回一个字符串
                String result = arrs[new Random().nextInt(3)];
                System.out.println(Thread.currentThread().getName() + "——字符串:" + result);
                return result;
            }
        });
        //创建新线程
        Thread thread = new Thread(futureTask);
        //启动线程
        thread.start();
        String result = futureTask.get();
        System.out.println(Thread.currentThread().getName() + "——新线程的返回值:" + result);
    }
}

  • 如果是 JDK 1.8 以上版本(含1.8),在不需要获得线程执⾏结果的情况下,推荐使⽤写法②匿名方式创建子对象或写法⑤Lambda ⽅式来创建线程,因为简洁。
  • 如果想要获取线程执⾏结果,推荐使用写法⑦ FutureTask + Callable 的⽅式来实现。

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

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

相关文章

免费CRM客户管理系统真的存在吗?不仅有,还有5个!

免费CRM客户管理系统真的存在吗&#xff1f;当然有&#xff01; 说到CRM客户管理系统&#xff0c;相信很多企业并不陌生&#xff0c;是因为CRM客户管理系统已经成为大多数企业最不可或缺的工具。但是对于很多小微企业和个人用户来说&#xff0c;购买和实施CRM的成本仍然难以承…

上海亚商投顾:沪指午后放量跳水两市上涨个股不足500只

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪指数早间震荡走高&#xff0c;沪指盘中收复3300点&#xff0c;午后集体跳水&#xff0c;创业板指一度跌超2%。ChatGP…

Spring 系列之 MVC

Spring 系列文章目录 文章目录Spring 系列文章目录前言一、介绍二、项目搭建1.创建空项目2.设置maven和lombok3.创建maven web module4. 配置Tomcat启动运行项目&#xff08;选择local本地&#xff09;5. 导入jar依赖包6.在web.xml中配置DispatcherServlet7. 加入SpringMVC的配…

第六章——抽样分布

文章目录1、统计量的定义2、常用的统计量3、经验分布函数4、正态总体常用统计量的分布4.1、卡方分布4.1.1、卡方分布的定义4.1.2、卡方分布的性质4.2、t分布4.2.1、t分布的定义4.2.2、t分布的性质4.3、F分布4.3.1、F分布的定义4.3.2、F分布的性质5、正态总体的样本均值与样本方…

Hexo搭建个人博客流程全记录

建站缘由 有些东西过不了审&#xff0c;不方便给他人查看&#xff0c;于是就利用Hexo框架搭建了一个个人博客&#xff0c;部署在Github上&#xff0c;取名为“zstar的安全屋”。 链接&#xff1a;http://xdxsb.top 安装Git Bash 无需多言&#xff0c;下载链接&#xff1a;ht…

全网招募P图高手!阿里巴巴持续训练鉴假AI

P过的证件如何鉴定为真&#xff1f;三千万网友都晒出了与梅西的合影&#xff1f;图像编辑技术的普及让人人都能P图&#xff0c;但也带来“假图”识别难题&#xff0c;甚至是欺诈问题。 为此&#xff0c;阿里安全联合华中科技大学国家防伪工程中心、国际文档分析识别方向的唯一顶…

【集合】JAVA基础篇(二)

【集合】JAVA基础篇&#xff08;二&#xff09;一、java常用集合1、Java集合接口的作用2、Java集合常用实现类的作用二、Collection 常用的方法三、List 集合接口1、ArrayList类的常用方法2、LinkList类中的方法3、Vector4、ArrayList 类和 LinkedList 类的区别四、Set 集合1、…

图解LeetCode——剑指 Offer 27. 二叉树的镜像

一、题目 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像&#xff0c;返回镜像后的根节点TreeNode。 二、示例 2.1> 示例 1&#xff1a; 【输入】root [4,2,7,1,3,6,9] 【输出】[4,7,2,9,6,3,1] 限制&#xff1a; 0 < 节点个数 < 1000 …

Hadoop核心组成和生态系统简介

一、Hadoop的概念 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下&#xff0c;开发分布式程序。充分利用集群的威力进行高速运算和存储。Hadoop实现了一个分布式文件系统&#xff08; Distributed File System&#xff09;&am…

Word处理控件Aspose.Words功能演示:使用 C++ 将电子邮件消息转换为 PDF

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c;API支持所有流行的Word处理文件…

数据结构基础(力扣算法)(数组、字符串、链表、栈、部分树)(1-16天计划)

数据结构基础数组136. 只出现一次的数字169. 多数元素15. 三数之和75. 颜色分类56. 合并区间706. 设计哈希映射119. 杨辉三角 II48. 旋转图像59. 螺旋矩阵 II240. 搜索二维矩阵 II334. 递增的三元子序列238. 除自身以外数组的乘积435. 无重叠区间560. 和为 K 的子数组字符串415…

基于TC377的MACL-ADC General配置解读

目录标题一、MACL-ADC General1.Config Variant与AdcConfigSet2. AdcGeneral3.AdcPublishedInformation二、最终对应达芬奇生成内容一、MACL-ADC General 1.Config Variant与AdcConfigSet Config Variant &#xff1a;变体配置&#xff0c;默认选择VariantPostBuild就好了&…

融云服务推动多款应用「登顶」海外下载榜!

移步【融云全球互联网通信云】&#xff0c;了解更多信息。 2023 开年第一爆&#xff0c;属于中国出海社交软件。 在全球互联网通信云领军品牌“融云”护航下&#xff0c;多款社交黑马一路登顶、持续霸榜。它们是在中东横空出世、一经发布便冲击沙特全品类下载榜首的 Beem&…

对话系统学习概述(仅够参考)

对话系统&#xff08;仅够参考&#xff09; 目录对话系统&#xff08;仅够参考&#xff09;背景类人对话系统的关键特征1、知识运用2、个性体现3、情感识别与表达数据集评价方式评价的一些指标训练模型需要的资源任务型对话系统预训练最新研究进展参考文献背景 对话系统一般包括…

Redis三 高级篇-3. 最佳实践

《Redis三 高级篇-3. 最佳实践》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《Redis三 高级篇-3. 最佳实践》《Redis三 高级篇-3. 最佳实践》1、Redis键值设计1.1、优雅的key结构1.2、拒绝BigKey1.2.1、BigKey的危害1.2.2、如何发现BigKey①redis-cli…

Redis 数据类型

我们知道 Redis 是 Key-Value 类型缓存型数据库&#xff0c;Redis 为了存储不同类型的数据&#xff0c;提供了五种常用数据类型&#xff0c;如下所示&#xff1a; string&#xff08;字符串&#xff09;hash&#xff08;哈希散列&#xff09;list&#xff08;列表&#xff09;…

多线程事务怎么回滚

背景介绍1&#xff0c;最近有一个大数据量插入的操作入库的业务场景&#xff0c;需要先做一些其他修改操作&#xff0c;然后在执行插入操作&#xff0c;由于插入数据可能会很多&#xff0c;用到多线程去拆分数据并行处理来提高响应时间&#xff0c;如果有一个线程执行失败&…

返回数组的上三角和下三角np.triu()和np.tril()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 返回数组的上三角和下三角 np.triu()和np.tril() 选择题 以下说法错误的是? import numpy as np anp.array([[1,2,3],[4,5,6],[7,8,9]]) print("【显示】a&#xff1a;\n",a) pri…

一、Python时间序列小波分析——实例分析

小波分析是在Fourier分析基础上发展起来的一种新的时频局部化分析方法。小波分析的基本思想是用一簇小波函数系来表示或逼近某一信号或函数。 小波分析原理涉及到傅里叶变换&#xff0c;并有多种小波变换&#xff0c;有点点小复杂。但是不会原理没关系&#xff0c;只要会应用并…

单臂路由配置

单臂路由实现不同VLAN间通信 链路类型 交换机连接主机的端口为access链路 交换机连接路由器的端口为Trunk链路 子接口 路由器的物理接口可以被划分成多个逻辑接口 每个子接口对应一个VLAN网段的网关 我们需要知道的是单臂路由和虚拟机软件原理比较相似。他们都是依托于物理设备…