多线程(初阶)——多线程基础

news2024/11/15 17:26:57

多线程(初阶)——多线程基础

文章目录

  • 多线程(初阶)——多线程基础
    • 1.认识线程
    • 2.多线程程序
      • 2.1 第一个Java多线程程序
      • 2.2 观察线程的详细情况
      • 2.3 sleep方法
      • 2.4 run和start方法的区别
    • 3.创建线程
      • 3.1 继承Thread类
      • 3.2实现Runnable接口
      • 3.3 通过匿名内部类创建线程
      • 3.4通过实现Runnable接口的匿名内部类的方式创建线程
      • 3.5通过Lambda表达式的方式创建线程(推荐使用)
    • 4.多线程的优点

1.认识线程

首先我们得知道线程是什么

进程内的执行单元,不分配单独的资源,执行一个单独的子任务。

线程是进程内调度和分派的基本单位,共享进程资源。每个线程有自己的独立的栈存储空间,保存线程执行的方法以及基本类型的数据。

为啥会有线程

  1. 我们需要进行“并发编程”(CPU单个核心已经发展到了极致,无法满足当前编程需求,想要提高算力,就要使用多个核心)
  2. 引入并发编程的目的就是更充分利用多核CPU资源
  3. 使用多线程可以做到并发编程,并且能够使CPU多核被充分利用
  4. 虽然多进程也能实现并发编程,但是线程比进程更轻量
    • 创建线程比创建进程更快
    • 销毁线程比销毁进程更快
    • 调度线程比调度进程更快

一个线程包含在进程之中(一个进程中可以有多个线程)

当我们创建一个进程时

  1. 创建PCB
  2. 分配系统资源(尤其是系统资源)——特别消耗时间
  3. PCB加入的内核的双向链表中

而创建线程时

  1. 创建PCB
  2. PCB加入到内核的双向链表中

节省了大量资源分配的时间,提高了效率

2.多线程程序

2.1 第一个Java多线程程序

Java中创建线程,离不开一个关键的类——Thread

JDK中提供了一个叫Thread的类,通过Thread类创建对象,就可以创建一个线程

public class Demo1 {
    public static void main(String[] args) {
        MyTread myTread = new MyTread();
        myTread.start();
    }
}

class MyTread extends Thread {
    @Override
    //重写run方法
    public void run() {
        System.out.println("thread...");
    }
}

image-20230126002310593

重写run方法,就是去指定线程要执行的任务

MyTread myTread = new MyTread();创建自己的线程对象,本质就是Java类的一个实例

myTread.start();这个方法让JVM去操作申请一个真实的PCB,这就与操作系统扯上关系了,操作系统就能执行我们自己定义的这个线程任务了。

2.2 观察线程的详细情况

当我们在主函数中添加一句这样的代码

public class Demo1 {
    public static void main(String[] args) {
        MyTread myTread = new MyTread();
        myTread.start();
        System.out.println("main...");
    }
}

class MyTread extends Thread {
    @Override
    //重写run方法
    public void run() {
        System.out.println("thread...");
    }
}

此时运行结果是

image-20230126002951090

代码先执行的是myTread.start();,但为什么先打印的是“main…”,而不是先打印“thread”呢?

public class Demo1 {
    public static void main(String[] args) {
        MyTread myTread=new MyTread();
        myTread.start();
        //在死循环中做打印
        while (true){
            System.out.println("main...");
            try {
                MyTread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
//通过继承Tread类的方式创建一个线程
class MyTread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("thread...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Thread.sleep(1000);让线程休息一会

当代码进行死循环打印时会出现下面的结果

image-20230126003503919

这时我们发现有时候先打印“main…”,有时候会先打印“thread…”

这是由于CPU调度线程是抢占式执行的,这个现象是程序猿无法控制的,完全是由CPU内部的执行机制造成的

线程的执行先后顺序,取决于操作系统、调度器的具体实现

我们可以在任务管理器中查看Java进程的情况(需要执行死循环操作)

image-20230126010414608

此时我们还看不到Java线程,这时我们需要借助JDK为我们提供的工具jconsole

image-20230126011115066

打开jconsole之后,我们就能查看线程的情况了(此时程序要处于运行状态)

image-20230126011446916

image-20230126011505393

image-20230126012227493

其他线程都是JVM自己创建的线程,例如:JC垃圾回收器

2.3 sleep方法

为了方便观察线程的详细情况,我们适当的让线程"休息"一下,减缓刚刚执行的死循环代码,我们可以用sleep来进行操作

sleep是"休眠"的操作,让线程进入"阻塞"状态,放弃占有CPU时间片,让给其他线程使用

使用方法:Thread.sleep();sleepThread静态成员方法,可直接通过 类名.方法名的方式调用

形参:毫秒

使用时需要使用try...catch...处理中断异常

2.4 run和start方法的区别

作用功能不同:

  1. run方法的作用是描述线程具体要执行的任务;
  2. start方法的作用是真正的去申请系统线程

运行结果不同:

  1. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
  2. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。

3.创建线程

3.1 继承Thread类

例如上面的代码,创建一个类继承Thread类,重写run方法

public class Demo1 {
    public static void main(String[] args) {
        MyTread myTread=new MyTread();
        //myTread.start();
        myTread.run();
        //在死循环中做打印
        while (true){
            System.out.println("main...");
            try {
                MyTread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
//通过继承Tread类的方式创建一个线程
class MyTread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("thread...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

3.2实现Runnable接口

public class Demo2 {
    public static void main(String[] args) {
        //实例化Runnable
        Runnable myRunnable=new MyRunnable();
        //通过Thread构造方法传入参数myRunnable
        Thread thread=new Thread(myRunnable);
        thread.start();

    }
}
class MyRunnable implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("MyRunnable");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Runnable 接口内唯一声明了 run 方法,由 Thread 类实现。使线程与任务分离开,可以更好的解耦合,“高内聚,低耦合”,使用实现Runnable接口的方法更优

让任务与线程分离,以便后面在修改代码时影响较小,就可以不用关注线程创建代码,只关心线程任务中的代码,方便代码的维护

新建相同任务的线程时,就不用重写run方法,直接将定义好的Runnable传入就可以了

3.3 通过匿名内部类创建线程

public class Demo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(){
            @Override
            public void run() {
                while (true){
                    System.out.println("通过创建Thread类的匿名内部类创建线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t1.start();
    }
}

3.4通过实现Runnable接口的匿名内部类的方式创建线程

public class Demo5 {
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                int count=0;
                while (true){
                    count++;
                    System.out.println("通过实现Runnable接口的匿名内部类的方式创建线程"+count);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
    }
}

3.5通过Lambda表达式的方式创建线程(推荐使用)

public class Demo6 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            int count =0;
            while (true) {
                count++;
                System.out.println("通过Lambda表达式的方式创建线程 " + count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
    }
}

4.多线程的优点

增加运行速度

分别使用串行和并行实现10亿次累加

public class Demo7 {
    private static final long count=10_0000_0000L;
    public static void main(String[] args) {
        //串行
        serial();
        //并行
        parallel();

    }

    //并行
    private static void parallel() {
        long begin=System.currentTimeMillis();
        Thread t1=new Thread(()->{
            long a=0L;
            for (int i = 0; i < count; i++) {
                a++;
            }
        });
        Thread t2=new Thread(()->{
            long b=0L;
            for (int i = 0; i < count; i++) {
                b++;
            }
        });
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end=System.currentTimeMillis();
        System.out.println("并行耗时:"+(end-begin)+"ms.");
    }
    //串行
    private static void serial() {
        long begin=System.currentTimeMillis();
        long a=0L;
        for (int i = 0; i < count; i++) {
            a++;
        }
        long b=0L;
        for (int i = 0; i < count; i++) {
            b++;
        }
        long end=System.currentTimeMillis();
        System.out.println("串行耗时:"+(end-begin)+"ms.");
    }
}

运行结果:

image-20230127002628511

这时我们可以看到并行的耗时明显小于串行的耗时,当让任务量大时,多线程的效率会提高不大,但是当任务量不大时,可能多线程的效率还没有单线程的效率高,毕竟创建线程时CPU的调度也是有开销的。

觉得不错就给个三连吧

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

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

相关文章

【目标检测】------yolox网络结构

YOLOX网络结构图 卷积和池化计算器&#xff1a; http://www.sqflash.com/cal.html

ServletAPI 2-10复杂参数, 解析完的参数值都会放到 ModelAndViewContainer里面

总结&#xff1a; 找到解析器后&#xff0c;用解析器去解析参数&#xff0c;都使用了resolveArgument()中以下方法 mavContainer.getModel(); mavContainer:模型和视图容器 视图&#xff1a;页面请求要返回一个地方&#xff0c;这个地方的地址叫视图 。比如要到/sucess 模…

CV——day70 零基础学YOLO:YOLOv1

YOLO系列1. 不同阶段算法优缺点分析2. IOU指标计算3. MAP指标计算**指标分析**如何计算MAP4 YOLOv14.1 YOLOv1核心思想4.2 YOLOv1网络架构那么&#xff0c;7 * 7 * 30的输出是怎么来呢&#xff1f;4.3 损失函数4.3.1 位置误差4.3.2 置信度误差(含object)4.3.3 置信度误差(不含o…

股票量化分析工具QTYX使用攻略——小市值轮动选股策略(更新2.5.9)

搭建自己的量化交易系统 如果要长期在市场中立于不败之地&#xff01;必须要形成一套自己的交易系统。 如何学会搭建自己的量化交易系统&#xff1f; 边学习边实战&#xff0c;在实战中学习才是最有效地方式。于是我们分享一个即可以用于学习&#xff0c;也可以用于实战炒股分析…

Springboot农产品特产销售网站maven mysql idea

摘 要 I 1 绪论 1 1.1研究背景 1 1.2研究现状 1 1.3研究内容 2 2 系统关键技术 3 2.1 JSP技术 3 2.2 JAVA简介 3 2.3 MYSQL数据库 4 2.4 B/S结构 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2经济可行性 5 3.1.3操作可…

[Android开发基础2] 七大常用界面控件(附综合案例)

文章目录 一、文本TextView 二、按钮Button 三、编辑输入框EditText 四、图片ImageView 五、单选按钮RadioButton 六、复选框CheckBox 七、系统消息框Toast 综合案例&#xff1a;账号注册界面 一、文本TextView TextView控件用于显示文本信息。 演示&#xff1a; <?…

kafka可视化web管理工具-KafkaMmap

kafka可视化web管理工具-KafkaMmap ​ 使用过kafka的小伙伴应该都知道kafka本身是没有管理界面的&#xff0c;所有操作都需要手动执行命令来完成。但有些命令又多又长&#xff0c;如果没有做笔记&#xff0c;别说是新手&#xff0c;就连老手也不一定能记得住&#xff0c;每次想…

blender绑定骨骼法 2 rig

image.pngimage.png生成一个body_temp add neck 脖子 add chin 下巴 image.pngadd shoulder肩膀 image.pngadd wrist手腕 add spline root .额那个位置 image.pngadd ankle脚踝 image.pngFACIAL setup是面部的骨骼绑定,这里没眼球啥的就不弄了直接 点go 就生成了骨骼. image.pn…

1、SPSS 25的安装

目录 一、简介 二、竞争优势 三、安装步骤 一、简介 SPSS 是一款用于Windows操作系统的数据和科学图形的数值分析软件&#xff0c;能够为用户提供多样的数据统计、处理、报告等功能&#xff0c;还能根据数据帮用户生成离散图、折线图等图形&#xff0c;以更加直观的方式向你…

【数据库增删查改基础版】本文将与大家一起去探索基础版本的数据库增删查改,图文讲解数据库增删查改

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#xff0c;这篇文章我将带领大家进入基础版本增删查改数据库的学习&#xff0c;后面的文章会有数据库进阶版&#xff0c;更多的约束条件让你润享丝滑。增删查改是数据库操作中极其重要的内容&#xff0c;我们从简单入手&#x…

12.Java二维数组讲解

本篇文章重点需要掌握的内容如下: 后面附有Java二维数组的练习题 文章目录前言一、二维数组的应用场景二、二维数组的初始化1.静态初始化2.动态初始化三、二维数组的内存图1.特殊情况1的写法:特殊情况1的写法的好处:2.特殊情况2的写法四、二维数组的练习题总结前言 一、二维数…

三子棋游戏

前言 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来&#xff0c;相对两边依次摆上三个双方棋子&#xff0c;只要将自己的三个棋子走成一条线&#xff0c;对方就算输了。但是&#xff0c;有很多时候会出现和棋的情况。 文…

Hi3861鸿蒙物联网项目实战:倒车雷达

华清远见FS-Hi3861开发套件&#xff0c;支持HarmonyOS 3.0系统。开发板主控Hi3861芯片内置WiFi功能&#xff0c;开发板板载资源丰富&#xff0c;包括传感器、执行器、NFC、显示屏等&#xff0c;同时还配套丰富的拓展模块。开发板配套丰富的学习资料&#xff0c;包括全套开发教程…

Cadence PCB仿真 使用 Allegro PCB SI 敏感网络列表.lst文件的建立方法图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,基本方法3,建立技巧4,总结1,概述 敏感网络表(.lst文件)是一个在PCB设计阶段需要特别关注的网络列表。本文简单介绍其建立或导出方法。 2,基本方法 第1步:使用记事本新建1个文本文档…

vue2原理核心Object.defineProperty()的理解

Object.defineProperty(obj, prop, { configurable:false //默认false ,为true是可以属性修改,删除 enumerable:false //默认false ,为true是可以枚举对象 value:prop对应的值 // 初始为undefined writable:false //默认false ,为true是可以修改value的值 }) o…

python实现XML文件的内容变更

文章目录一、使用背景二、实现效果三、内置库的使用四、代码实现解析五、完整代码一、使用背景 我们在某些情况下需要对XML格式的文件进行内容处理&#xff0c;可以直接打开处理&#xff0c;但是对于一些需要自动化或者不方便手动处理的情况下我们的代码实现就比较关键啦&…

maxlength属性

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>maxlength属性</title> </head> <body> <form action"action_page.php"> <!--…

面试官问:生产环境遇到难题,你是如何解决的?

1、生产环境发生 cpu 飙高的问题&#xff1f;你是如何定位解决的呢&#xff1f; 我们的线程是运行在 cpu 上面 1. CAS 自旋一直重试导致 cpu 飙高 没有控制自旋次数&#xff1b;乐观锁 2. 死循环; 3. 阿里云 Redis 被注入挖矿程序,建议 Redis 端口不要能够被外网访问; 4. 服务…

Python 介绍和环境准备

文章目录一、概述二、Python 应用领域1&#xff09; Web应用开发2&#xff09;自动化运维3&#xff09;网路爬虫4&#xff09;人工智能领域5&#xff09;科学计算三、Python 环境准备1&#xff09;Linux 平台安装 Python2&#xff09;Window 平台安装 Python3&#xff09;安装 …

Windows WSL 启用 Systemd(使用 snap 安装应用)

背景 如今&#xff0c;WSL 已经能够支持 systemd。我们看一下怎么在 WSL 启用 systemd 来使用 snap。 准备工作 确保 WSL 版本在 0.67.6 及以上。可以通过如下命令查看。 wsl --version如果版本过低&#xff0c;可以下载最新版本的 WSL 并安装。 启用 Systemd 测试环境为…