【多线程初阶】第一次认识线程

news2024/11/16 17:32:12

多线程初阶系列目录

持续更新中

1.第一次认识线程


文章目录

  • 多线程初阶系列目录
  • 前言
  • 1. 线程概念
    • 1.1 线程是什么
    • 1.2 为什么需要线程
    • 1.3 进程和线程的区别
    • 1.4 Java线程和操作系统线程的关系
  • 2. 第一个Java多线程程序
  • 3. 创建线程的方法
    • 3.1 继承 Thread 类
    • 3.2 实现 Runnable 接口
  • 总结


前言

本文讲解内容为多线程的基础内容, 带大家了解一下什么是线程, 线程与进程的区别是什么, 以及创建第一个Java多线程程序.

在进行线程的学习前, 可以先简单了解一下操作系统中进程的概念, 有助于更好的理解线程.

关注收藏, 开始学习吧🧐


1. 线程概念

在正式讲解线程前, 我们先聊一下进程, 操作系统中引入进程, 目的是为了能够实现多个任务, 并发执行的效果. 但是进程是比较重量级的, 有一个问题, 如果频繁的创建或者销毁进程, 由于操作系统的不断进行资源分配, 其所消耗的成本是比较高的.

那么操作系统是如何进行优化的呢, 这就要说到我们今天的主角 ---- 线程. 其实进程当中是包含线程的, 一个进程里可以有一个线程, 或者多个线程. 而每个线程都是一个独立的执行流, 多个线程之间, 也是并发执行的.

那么线程是怎么减轻重量的呢? 在一个进程中的多个线程之间, 是共用一份系统资源的. 如内存空间, 文件描述符表等等, 只有在进程启动, 创建第一个线程的时候, 需要花成本去申请系统资源, 一旦进程 (第一个线程) 创建完毕, 再创建新的线程, 也不必再申请资源了, 这样创建和销毁的效率就提高了不少.

操作系统真正调度的是线程, 而不是进程. 请大家时刻牢记下面两个概念:

  • 线程 ---- 操作系统 调度运行 的基本单位
  • 进程 ---- 操作系统 资源分配 的基本单位

1.1 线程是什么

一个线程就是一个 “执行流”. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 “同时” 执行着多份代码.

我们不妨设想一个场景去理解:

一家公司要去银行办理业务, 既要进行财务转账, 又要进行福利发放, 还得进行缴社保.

如果只有张三一个会计就会忙不过来, 耗费的时间特别长. 为了让业务更快的办理好, 张三又找来两位同事李四, 王五一起来帮助他,
三个人分别负责一个事情, 分别申请一个号码进行排队, 自此就有了三个执行流共同完成任务, 但本质上他们都是为了办理一家公司的业务.

此时, 我们就把这种情况称为多线程, 将一个大任务分解成不同小任务, 交给不同执行流就分别排队执行. 其中李四, 王五都是张三叫来的,
所以张三一般被称为主线程 (Main Thread).

再用我们本身的计算机去举一个简单例子:

在同一个程序中, 内部想要并发的完成多组任务, 此时就使用多线程比较合适, 因为可以更高效, 更节省资源.
比如我们计算机中有一个直播软件, 这是一个程序, 它需要同时录制画面, 录制声音, 进行网络传输等等. 这些程序中的功能, 其实就是一个个的线程, 也就是多线程.
而多个程序之间, 就是多个进程了, 比如该直播软件是一个进程, 而打开QQ后, QQ也是一个进程. 由于隔离性, 两个进程间是互不影响的.

1.2 为什么需要线程

首先, “并发编程” 成为 “刚需”.

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源.
  • 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.

其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量.

  • 创建线程比创建进程更快.
  • 销毁线程比销毁进程更快.
  • 调度线程比调度进程更快.

最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine).

关于线程池我们后面再介绍. 关于协程的话题我们此处暂时不做过多讨论.

1.3 进程和线程的区别

下面我们举一个更贴近生活的案例来讲解:

有一个滑稽老铁在拧100个螺丝, 效率是比较低的.
在这里插入图片描述
如果我们采用多进程的方式来改进一下, 虽然也可以提高效率, 但这样就相当于多准备一间房间, 多准备一个桌子, 并且此时双方也看不到对方的进度.
在这里插入图片描述
而如果采用多线程的方式, 此时就相当于有两个滑稽老铁(两个线程), 房间和桌子也都只需要准备一个, 不需要额外准备空间, 拧螺丝的效率一样可以大大提升, 并且也都能看到对方的进度.
在这里插入图片描述
接下来, 我们进一步提高滑稽老铁的个数, 此时分给每个滑稽老铁的螺丝越来越少了, 拧螺丝的效率越来越高了.
在这里插入图片描述
但是多线程也有一些问题, 放在我们这个例子中, 如果当滑稽老铁数量越来越多后, 房间挤的满满的, 在挤来挤去的过程中, 把拧螺丝的桌子给弄翻了, 这样就会导致整个拧螺丝的工作都作废了.

所以多线程中当一个线程出现异常了, 此时很容易将整个进程都弄奔溃, 其他线程也随之崩溃了.

在进行了举例子的讲解后, 我们再给大家一些书面概念.

进程和线程的区别:

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程.
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.
  • 进程之间具有独立性, 一个进程挂了, 不会影响到别的进程. 同一个进程里的多个线程之间, 一个线程挂了, 可能会把整个进程都带走, 并影响其他的线程.
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位.
    在这里插入图片描述

1.4 Java线程和操作系统线程的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.

2. 第一个Java多线程程序

多线程程序和普通程序的区别:

  • 每个线程都是一个独立的执行流
  • 多个线程之间是 “并发” 执行的.

Java 标准库中提供了一个类 Thread 能够表示一个线程, 先给大家看一个多线程程序.

import java.util.Random;

public class ThreadDemo {

    private static class MyThread extends Thread {
        @Override
        public void run() {
            Random random = new Random();
            while (true) {
                // 打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    // 随机停止运行 0-9 秒
                    Thread.sleep(random.nextInt(10000));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public static void main(String[] args) {

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

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

        Random random = new Random();
        while (true) {
            // 打印线程名称
            System.out.println(Thread.currentThread().getName());
            try {
                // 随机停止运行 0-9 秒
                Thread.sleep(random.nextInt(10000));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

}

使用 jconsole 命令观察线程

可以在自己 java 目录下的 bin 目录中找到, 博主给一个我自己的地址供大家参考. 使用这个jdk提供的工具, 就能够给我们查看出 Java 进程里面的线程详情.
C:\Program Files\Java\jdk1.8.0_192\bin

进入bin目录后, 找到jconsole.exe文件, 双击打开.
在这里插入图片描述
打开后, 我们选择自己刚才运行的线程并进行连接. 注意, jconsole 只能分析 Java 进程, 不能识别非 Java 写的进程. 并且要确保你自己的代码正在运行.
在这里插入图片描述
然后我们点击不安全的连接即可, 不用担心安全问题.
在这里插入图片描述
在这里插入图片描述
这样我们就看到了该程序中创建的三个线程, 以及一个 main 方法的主线程.
在这里插入图片描述

3. 创建线程的方法

3.1 继承 Thread 类

  1. 继承 Thread 类来创建一个线程类
class MyThead extends Thread {
    // 需要重写run()方法
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t!");
            try {
            	// 休眠 1s
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  1. 创建 MyThread 类的实例
// 向上转型
Thread t = new MyThread();
  1. 调用 start 方法启动线程
// 线程开始运行
t.start();

完整代码

class MyThead extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThead();
        t.start();
        while (true) {
            System.out.println("hello main");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

实现效果:
在这里插入图片描述
可以看到, main 方法的主线程, 与我们创建的 t 线程在同时执行.

3.2 实现 Runnable 接口

  1. 实现 Runnable 接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  1. 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
  1. 调用 start 方法
t.start();

完整代码

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo2 {

    public static void main(String[] args) {

        MyRunnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);

        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

对比上面两种方法:

  • 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
  • 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()

其他变形

  • 匿名内部类创建 Thread 子类对象
public class ThreadDemo3 {

    public static void main(String[] args) {

        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}
  • 匿名内部类创建 Runnable 子类对象
public class ThreadDemo4 {

    public static void main(String[] args) {

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}
  • lambda 表达式创建 Runnable 子类对象

lambda 表达式, 本质上是一个匿名函数.
lambda 表达式的基本写法

() -> {

}

()里面放参数, 如果只有一个参数, ()可以省略
{}里面放函数体, 可以写各种 Java 代码, 如果只有一行代码, 也可以省略{}

public class ThreadDemo5 {

    public static void main(String[] args) {

        Thread t = new Thread( () -> {
            while (true) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

总结

✨ 本节目标主要为,认识多线程并掌握多线程程序的编写, 重点需要掌握进程与线程的区别, 以及创建线程都有哪些方法. 本人还会继续更新多线程相关内容, 请持续关注.
✨ 想了解更多的多线程知识, 可以收藏一下本人的多线程学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

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

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

相关文章

深兰科技发布《深兰数字智能产业系列报告(2023年):个人数字化》

近日,深兰科技发布《深兰数字智能产业系列报告(2023年):个人数字化》,这是深兰科技在数字智能产业领域发布的首份研究报告,也是国内第一份个人数字化产业报告。此报告是在上海市经济和信息化委员会和上海市产业技术创新促进会的共…

星辰天合公司产品完成阿里云 PolarDB 数据库产品生态集成认证

近日,XSKY星辰天合旗下产品与阿里云 PolarDB 开源云原生数据库展开产品集成认证测试,并获得阿里云颁发的产品生态集成认证证书。 测试结果表明,星辰天合旗下的融合计算管理平台 XHERE(V2)、统一数据平台 XEDP&#xf…

appuploder全过程使用教程(Windows版本)

appuploder全过程使用教程(Windows版本) 转载:使用appuploader工具流程(Windows版本) 一.登录apple官网,注册账号 1.注册苹果账号 Sign In - Apple 2.登录开发者中心 ,出现协议弹框&#xf…

Android 画面显示流程一

DRM,英文全称 Direct Rendering Manager, 即 直接渲染管理器。 DRM是linux内核的一个子系统,提供一组API,用户空间程序,可以通过它,发送画面数据到GPU或者专用图形处理硬件,也可以使用它执行诸如配置分辨率,刷新率之类的设置操作。原本是设计提供PC设备支持复杂的图形设。…

浏览器配置环境

疯掉了 希望是最后一次 0.配置WinSCP和PUTTY 在Windows上使用PuTTY进行SSH连接-腾讯云开发者社区-腾讯云 1.配置conda 如何在Linux服务器上安装Anaconda(超详细)_linux安装anaconda_流年若逝的博客-CSDN博客 实验室远程登录Linux服务器并配置环境_远…

docker容器更改映射端口

问题描述: 我们在docker中开启nginx以后,如果这时候在nginx中配置除了80以外的端口的监听,会发现无法访问,这时候其实是因为我们没有开启端口映射导致的。 目前发现有两种解决方案 如下: 目录 一. 修改配…

Android AIDL基本使用

AIDL是Android多进程通讯方式一种。 如要使用 AIDL 创建绑定服务,请执行以下步骤: 创建 .aidl 文件 此文件定义带有方法签名的编程接口。 实现接口 Android SDK 工具会基于您的 .aidl 文件,使用 Java 编程语言生成接口。此接口拥有一个名为…

树莓派Pico与MicroSD卡模块接口及MicroPython编制读写MicroSD存储卡程序

介绍树莓派(RPi)Pico开发板(或树莓派Pico W无线开发板)与MicroSD卡模块SPI接口技术原理及SPI接口硬件连接的具体步骤,讲述采用MicroPython和SDCard类编制程序读写MicroSD存储卡的方法,给出读写MicroSD存储卡文件的测试程序。 一、…

创建数据库Market、Team,按要求完成指定操作

创建数据库Market,在Market中创建数据表customers,customers表结构如表4.6所示,按要求进行操作。 代码如下: #(1)创建数据库Market mysql> create database Market; Query OK, 1 row affected (0.00 sec)mysql>…

Windows系统中将markdown文件批量转化为PDF

需要将一个文件夹下的多个md文件转化为PDF 下载安装pandoc 官网下载地址:Pandoc ,下载位置如下图。 下载后按照默认文件路径安装完成 使用everything软件查找pandoc.exe文件路径,如下图: 安装完成之后就可以在cmd窗口或Window…

Cyclo(L-Asp-L-Gly),52661-97-9,定制含D型与L型氨基酸

(文章资料汇总来源于:陕西新研博美生物科技有限公司小编MISSwu) 试剂基团反应特点(Reagent group reaction characteristics): Cyclo(L-Asp-L-Gly),52661-97-9,(活性药物…

荧光染料92557-81-8,FAM NHS ester,6-isomer,用于标记核苷酸的荧光试剂

6-羧基荧光素琥珀酰亚胺酯 6-FAM, 用于标记核苷酸的荧光试剂,6-羧基荧光素琥珀酰亚胺酯是一种化学物质。荧光标记染料在生物分析中发挥越来越重要的作用,特别是6-羧基荧光素已经成为检测蛋白质,DNA序列的国际通用的荧光染料。 6-F…

变压器绝缘电阻测试试验

试验目的 电力变压器是发电厂、 变电站和用电部门最主要的电力设备之一, 是输变电能的电器。 测量绕组绝缘电阻、 吸收比和极化指数, 能有效地检查出变压器绝缘整体受潮, 部件表面受潮脏污, 以及贯穿性的集中行缺陷, …

Revit柱的绘制:陶立克柱绘制方法和生成柱

​  一、Revit陶立克柱的绘制方法 BIM等级考试一级第十期第四题陶立克柱该如何绘制呢?接下来我演示给大家。 陶立克柱看似很复杂,其实很简单,只需要先绘制好柱身,会用到阵列工具的使用再绘制柱子上部或下部分,最后采用镜像工具…

spring系列所有漏洞vulhub复现CVE-2022-22978、CVE-2022-22963、CVE-2022-22965、CVE-2018-1273

文章目录 CVE-2022-22978 Spring-security 认证绕过漏洞漏洞描述:复现: CVE-2022-22963漏洞描述:复现: 提提神Spring框架Data Binding与JDK 9导致的远程代码执行漏洞(CVE-2022-22965)漏洞描述:复现: Spring Data Commo…

智慧水务信息化建设——看“厂、站、网”一体化综合监管

平台概述 智慧水务信息化平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心,以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑,以城市水资源、水生态、水环…

Intellij IDEA突然无法启动问题

遇到的情况:Intellij IDEA 双击或者鼠标右键右键单击都无法启动,打开任务管理器也没有Intellij IDEA线程启动。 解决方法: 第一步:以管理员身份打开命令提示符,输入命令(“ netsh winsock reset ”),关闭…

机器学习15:神经网络-Neural Networks

神经网络是特征交叉的更复杂版本。本质上,神经网络会学习适当的特征组合。本文主要介绍神经网络的结构、隐藏层、激活函数等内容。 目录 1.神经网络:结构 2.隐藏层 3.激活函数 3.1 常用激活函数 3.2 小结 4.神经网络小练习 4.1 第一个神经网络 …

异步请求(Ajax,axios,json)

同步/异步请求 表单(前端)向后端发送请求,属于同步请求 同步: 发一个请求, 给一个回应, 会用回应的内容覆盖掉浏览器中内容,这样会打断前端其他的正常操作,在现在的前端中,显得不太友好。 异步: 不同步 前端正常输入时…

腾讯云部署vitepress,高颜值文档博客

首先上官方网站。https://vitepress.dev/ 先看两张效果图。 一定要用这个官网,之前看了一个翻译版本,好像翻了一半不弄了,坑了半天时间也解决不了。目前看起来还没有官翻。 目前使用到的是腾讯云的轻量应用服务器,效果还是非常好…