初识多线程

Opencv相机校准之棋盘格标定 Reference: https://yq.aliyun.com/articles/62472 阅读详情

1)线程的创建(五种方式)

1.创建一个继承Thread的类

创建一个子类 重写run方法 run方法中写该线程需要执行的内容 继承Thread 实现这个子类

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("创建类继承Thread,重写run");
    }
}

2.创建实现Runnable接口的类

创建一个类,实现Runnable接口,之后创建这个类的实例,创建一个Thread对象,其构造方法中
传入刚刚创建的类的实例,实现执行代码和多线程分开

class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println("创建Runnable类,实现Runnable接口,重写run");
    }
}

3.创建Thread实例,使用匿名内部类

可以直接在主线程中创建一个Thread的实例,在括号后直接加一个大括号,重写run()方法

        Thread t2 = new Thread(){
            @Override
            public void run() {
                System.out.println("使用匿名内部类,创建Thread");
            }
        };

该种方法相当于直接创建了一个继承Thread的匿名内部类,重写run()方法,和第一种方法本质上一样。

4.创建Thread实例,在Thread类的构造方法中传入一个Runnable 实例 创造一个Runnable的匿名内部类

        Thread t3 = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("使用runnable的匿名内部类");
            }
        });

该种方法相当于创建了一个Runnable实例,将该实例传给了Thread 的构造方法,本质上与方法二一样。

5.使用lambda表达式简化创建

我们在创建Thread实例的时候使用lambda表达式,这样在lambda表达式种直接相当于重写了run()方法

        Thread t4 = new Thread(() -> {
            System.out.println("使用lambda表达式创建的Thread");
        });

这种方法本质上与第四个方法一样,其实是使用lambda表达式代替了Runnable实例

总结:

在创建新线程时,我们推荐使用带有Runnable的方法,因为Runnable中保存的是线程实际执行的代码,将其作为实例传送到Thread 中,可以更好的实现线程和线程中执行代码的分离,在想要修改执行代码内容的时候可能更为方便一点。同时,我们真正在操作系统中想要创建新线程,必须要调用start()方法,否则,我们的线程其实是没有被创建出来的。

2)线程的命名

我们在查看Thread构造方法时,可以看到
在这里插入图片描述
这个构造方法的意思是,创建一个线程并且命名,在我们命名线程后,可以更容易的分辨出来它,我们可以用Java自带的jConsole.exe文件来查看进程
中的各个线程,在这里就不过多演示了。

3)多线程的利用

在我们之前的了解中,可以知道,要是将进程比作一个工厂,那么线程相当于工厂中的流水线,我们想要提高工作效率,可以多建造几个同时工作的流水线,这样其实相当于更好的利用了多核的CPU。那么我们现在就介绍一个多线程的好处。

例子

当我们想要实现两个数从零自增到一个很大的数的时候,换做我们之前的思路,我们可以在main方法中写两个循环,就像这样

    public static void main(String[] args) {
        long a = 0;
        long b = 0;
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10_0000_0000; i++) {
            a++;
        }
        for (int i = 0; i < 10_0000_0000; i++) {
            b++;
        }
        long end = System.currentTimeMillis();
        System.out.println("方法一耗时:" + (end - start) + "ms");
    }

我们可以计算一下这种方法消耗的时间,在上述代码中使用了System.currentTimeMillis()方法,可以得到当前的时间(以毫秒为单位)
那么我们运行程序,得到
在这里插入图片描述
也就是说,我们的程序自上而下串行执行,需要消耗49ms左右。
但是我们可以使用第二个方法,创建两个线程,线程一负责a的增加,线程二负责b的增加,我们这个时候来看一下消耗时间

        long start1 = System.currentTimeMillis();
        Thread t1 = new Thread(() -> {
            long number = 0;
            for (int i = 0; i < 10_0000_0000; i++) {
                number++;
            }
        });
        Thread t2 = new Thread(() -> {
            long number = 0;
            for (int i = 0; i < 10_0000_0000; i++) {
                number++;
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        long end1 = System.currentTimeMillis();
        System.out.println("方法二耗时:" + (end1 - start1) + "ms");
    }

在这里插入图片描述

我们可以看到,方法二竟然比方法一慢了,这是为什么呢,和我们预料到的不一样,按理来说,两个线程并发执行,应该比方法一这种串行执行要快,尽管快不到百分之百(在操作系统底层,并不是一直并行,而是并发和并行都会存在,这样就不可能提升百分之一百的效率),但是也不该慢啊,事实上,这是因为我们的数太小了,我们创建线程同样也是需要时间的,这个创建新线程所消耗的时间占了任务的大部分时间,这样就得不偿失了。因此,多线程更适用于任务量比较大的程序之中。

4)线程的中断

我们刚刚提到了,线程创建中最重要的是重写run()方法,因为我们的线程中执行的就是run()方法中的代码,那么中断线程,本质上就是使得线程的run()方法中断,我们可以使用几种方式来中断线程。

1.自定义标志位

我们可以在线程中利用我们自定义的标志位,例如

    public static boolean flg = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(flg){
                System.out.println("thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(5000);
        flg = false;
    }

在这里插入图片描述
我们可以看到,在我们的定时5000ms,也就是输出了五次thread!后,线程中断了,这时,我们通过控制flg 就可以中断线程了。

2.利用Thread类中自带的标志位

1)Thread.currentThread().isInterrupted(); .currentThread()方法可以得到线程的实例,是一个实例方法,比较推荐
2)Thread.interruputed()方法 该方法是一个静态方法(因为该种方法属于Thread的静态成员,一个进程中只能有一个,因此会造成很多误会)

        Thread t1 = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("thread1!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        Thread.sleep(4000);
        t1.interrupt();
    }

在这段代码中,我们使用了线程实例名.interrupt()方法,这个方法就是设置这个实例所创建的线程的标志位,中断该线程,但是我们在运行的途中,发现系统报出异常
在这里插入图片描述
这是为什么呢,其实这是因为我们在设置标志位时会有两种情况,如果线程处于正常的就绪态,那么我们设置标志位为true后,线程中断,但是我们在线程中有sleep()方法,这个方法是使线程进入阻塞状态,阻塞时间为1000ms,那么这就意味着我们的线程很大一部分时间都处在阻塞状态中,这时我们设置标志位的话,就会触发异常,并且,我们的标志位也没有设置成功,程序还在进行,那么解决这个的方法是什么?其实我们不难发现

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

出问题的是这段代码,那么我们完全可以在抓到这个异常的时候,直接给它break,或者执行一些代码再break,这样就可以达到一个中断进程的目的了。

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("收尾工作!");
                    break;
                }

我们这时,再次运行发现
在这里插入图片描述
在捕捉到异常后,程序执行收尾工作后停止了运行,达到了我们的目的。

5)线程等待

我们在这里同时创建两个线程,让他输出不同的东西,来看看有什么发现

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("thread1!");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            System.out.println("thread2!");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();

在这里插入图片描述
我们竟然发现,t2连续运行了两次,按照我们之前的习惯,不应该是t1先运行一次,t2再运行一次,这样交替运行吗,但是事实不然,这是因为系统在调用线程时,优先级并不固定,是随机调用的,但是我们在有些需求中,可能需要线程交替运行,或者某一线程先运行完,再运行另一个线程,这个时候,我们就要用到线程等待的方法,join()方法。

join()方法介绍:

线程名1.join()这个方法,是调用join()方法这个线程,等待线程名1对应的线程先结束,之后再继续执行,这么说可能很绕,我们结合代码来看一下

        long start1 = System.currentTimeMillis();
        Thread t1 = new Thread(() -> {
            long number = 0;
            for (int i = 0; i < 10_0000_0000; i++) {
                number++;
            }
        });
        Thread t2 = new Thread(() -> {
            long number = 0;
            for (int i = 0; i < 10_0000_0000; i++) {
                number++;
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        long end1 = System.currentTimeMillis();
        System.out.println("方法二耗时:" + (end1 - start1) + "ms");
    }

我们在提及线程的利用时,我们使用过join方法,为了计算出准确的线程执行完的时间,我们相当于在main线程中,写了t1.join(),这意味着main方法执行到这一行的时候,程序暂停了下来,在等待t1线程执行完毕之后,再执行t2线程,在等待t2线程执行完毕之后,再执行计算时间的操作,这样就是实现了main线程等待t1,t2线程等待的情况,实现了线程等待。
join方法本质上是死等线程结束,比方说

        Thread t1 = new Thread(() -> {
            while(true){
                System.out.println("thread1!");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while(true){
                System.out.println("thread2!");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();

当我们在t2线程中先加一个t1.join,让t2线程等待t1线程执行完再开始执行,那么即使我们t2.start(),也不会输出thread2!,这就是死等,t1不结束,t2就不开始。
在这里插入图片描述
这时我们只需要在join()方法的括号中加一个等待时间,就可以实现等待一段时间后就不等了,比方说join(2000),那么预期效果应该是先输出四个threa1!后输出thread2!
加粗样式
我们发现确实如此。这就是线程的等待,join方法。

6)线程休眠

线程休眠事实上就是使线程暂时到阻塞态之中,过一段时间再调整到就绪态中,我们要利用的是sleep()方法,sleep中加入指定的时间(ms为单位),在之前我们已经使用过多次,这里就不仔细介绍了,我们需要知道的是,线程休眠实质上是将线程所对应的额PCB(process control block)从就绪态队列中调整到阻塞态队列中。

7)获取当前的线程实例

Thread.currentThread()方法,可以得到当前的实例,在哪个线程中使用这个方法,就可以得到当前线程的实例
在这里插入图片描述
我们在t线程中调用了Thread.currentThread(),得到的就是t线程的名字,
在main线程中调用则得到了main的名字,只是因为优先级的不确定先输出了main,其实,如果我们采用创造线程中的1.3方法时,完全可以将t线程中的Thread.currentThread()方法完全可以换成this,效果是一样的,但是我们在使用带有Runnable的表达式时(包含图中的利用lambda表达式创建线程),不可以用this来代替,因为这个时候的this指代的时Runnable实例,而非Thread实例。

无线自组织网络基础知识 参考书籍:《无线自组织网络路由协议及应用》 目录第一章 绪论1.1 无线自组织网络概述1.1.1 无线自组织网络的定义及分类1.1.2无线自组织网络的主要特点1.1.3无线自组织网络的应用与发展1.无线自组织网络的应用2.无线自组织网络的起源和发展1.2典型的无线自组织网络1.2.1移动Ad Hoc网络1、移动Ad Hoc网络的定义2.移动AdHoc网络的特点3.移动Ad Hoc网络的应用1.2.... 阅读详情

相关推荐

《uni-app 长列表优化:虚拟列表(vue-virtual-scroller)解决 1000+ 数据渲染卡顿》(附虚拟列表封装与多端适配)

本文详细介绍了虚拟列表技术在前端长列表渲染中的优化方案。针对传统v-for渲染导致的性能问题(内存占用高、渲染阻塞、滚动卡顿),提出基于vue-virtual-scroller的虚拟列表解决方案,通过可视区域渲染和DOM复用实现性能突破。文章包含核心原理、组件封装、多端适配及性能对比,并给出常见问题的解决方法。实践表明,该方案可将1000条数据的首次渲染时间从3800ms降至420ms,滚动FPS提升至55帧,显著改善用户体验。

qq_59041750的博客 2107

多线程基础系列-多线程初识

多线程的初步介绍,包含 线程和进程、线程的状态、线程的创建

囚徒之困 1235

如何使用JMeter进行大模型的压力测试?

使用JMeter进行大模型的压力测试,核心是通过模拟高并发请求,测试大模型在极限负载下的性能表现(如响应时间、吞吐量、错误率、资源瓶颈等)。通过以上步骤,可系统性评估大模型在高压下的性能极限,为优化模型(如量化、蒸馏)、扩容硬件(增加GPU)或调整接口限流策略提供数据依据。)、请求方式(POST)、认证方式(API Key放在Header)、请求参数(作用:避免瞬间高压压垮系统,更精准找到瓶颈点。等)、响应格式(同步/流式)。需获取:API地址(如。通过CSV文件引入不同。

alankuo的专栏 788

多线程(一):初识多线程

【前言】     浩浩荡荡的ITOO_Net 4.1版本号结束。回忆在整个征程,还算充实——在參加系统的研发之余,也有幸能參加到对多线程这个技术研究中去。并终于让其用到了项目中。     在这次研究过程中自己也对多线程以及相关的知识进行了深入的探索,总的来说还是蛮不错的一次探索之旅吧! 【前世今缘】     说起我和多线程的渊源,还得追溯到两年前的考试维护,记得当时自己还是一个...

weixin_34220963的博客 171

Field-Oriented Control (FOC) 矢量控制 磁场定向控制

Field-Oriented Control (FOC) 磁场定向控制(FOC),也称为矢量控制,是一种用于控制永磁同步电动机(PMSM)和交流感应电动机(ACIM)的技术。 FOC在整个扭矩和速度范围内都具有良好的控制能力。 FOC的实现需要将定子电流从固定参考系转换为转子磁通参考系(也称为d-q参考系)。 速度控制和转矩控制是FOC最常用的控制模式。位置控制模式不太常见。大多数牵引应用使用转矩控制模式,在该模式下,电动机控制系统遵循参考转矩值。在速度控制模式下,电机控制器遵循参考速度值,并生成用于形成内

>>> 1万+

MicroCap声学仿真介绍(六)-入耳式耳机仿真

从上图可以看出,后腔开了泄声孔后,振膜的最大位移从4.8um提升到了10um,振膜位移增加了一倍。在实际应用中,一般耳机的前腔和后腔都需要开泄声孔,开孔的大小和调音网布的阻尼需要根据客户的要求以及仿真的结果进行权衡选择。接着在主界面放置电阻、电感、电容,电阻值为Rms,电感值为Mms,电容值为Cms,如下图。假设后腔泄声孔的直径为1.2mm,宽度为0.3mm,对应调音网的阻抗为5.5e8欧姆。假设前腔泄声孔的直径为1.2mm,宽度为0.3mm,对应调音网的阻抗为5.5e8欧姆。

山无忧的博客 1800

我这样理解多线程之一-初识多线程

1 线程的基本概念理解 2 线程的生命周期 3 线程的创建方式

编程界小学生的博客 409

基于STM32设计的数字电子秤

当前项目是采用采用STM32+称重模块+OLED实现了简单的电子秤项目,称重模块上采用24位的ADC芯片,精度较高。实现了称重,校准、去皮等功能。

1万+

c++多线程初识

本文主要讲c++多线程thread的使用和调用和多线程的基础知识,也是为下一篇多线程开源库tbb做铺垫

m0_54069809的博客 1188

一文掌握 Playwright 的详细使用

Playwright 是一个功能强大的浏览器自动化工具,适用于网页自动化测试、数据抓取和网页交互。通过掌握其基础用法和高级功能,你可以轻松实现各种浏览器自动化任务。Playwright 支持多浏览器、多语言,并且具有高效的异步操作能力,是现代浏览器自动化的首选工具之一。

数据知道的博客 1万+

C#每日面试题-属性和字段的区别

在C#面试中,“属性(Property)和字段(Field)的区别”是基础且高频的考点。很多初学者会把两者混为一谈,甚至觉得“属性就是加了get/set的字段”,但这种理解只停留在表面。面试考察的核心,是对“封装思想”的理解,以及两者在语法特性、底层实现、实际应用中的本质差异。今天我们从“基础认知→核心差异→底层原理→实际应用→面试避坑”五个层面,把这个知识点讲透,既保证新手能看懂,又能满足面试的深度要求。

Amon的专栏 778

Linux多线程初识

*线程(Thread)**是进程内部的独立执行流,是CPU调度的基本单位。关键特性每个进程至少包含一个线程(主线程)线程在进程地址空间内运行,共享进程资源在Linux中,线程表现为轻量级的进程控制块(PCB)透过进程虚拟地址空间,可以看到进程的大部分资源,讲进程资源合理分配给每个执行流,就形成了线程执行流。

2301_79969685的博客 995

【回眸】Tessy集成测试软件使用指南(一)新手使用篇

这个专栏的文章前4篇都在记录如何使用Tessy进行单元测试,集成测试需要有一定单元测试基础,且做集成测试之前,需要做好单元测试,否则将会大幅增加软件单元代码纠错的代价。集成测试所花费的时间通常远远超过单元测试。如果直接从单元测试到系统测试,结果往往是不理想的。集成测试可以检测由于单元相互交互而导致的错误,通常由一组专业的测试人员执行。

啤酒鸭的物联网开发记录博客 5278

多线程概念初识

Java并发编程的“基石”——多线程概念初识 课程链接:链接 第1章 踏上Java并发征途 1-1 课程概览 晦涩难懂的概念: 线程、进程 串行、并行、并发 多线程、高并发 同步异步、阻塞非阻塞 问题 线程和进程有什么不同? 并行和并发有什么区别? 多线程是不是就是高并发? 同步阻塞 和 异步非阻塞 的关系? 1-2 从思维导图看并发脉络 第2章 孪生兄弟:进程和线程 2-1 什么是进程?实操案例:用活动监视器看正在运行的各进程 操作系统是包含多个进程的容器,而每个进程又都是容

weixin_42638946的博客 308

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【多线程】网络原理初识

【多线程】网络原理初识_菜菜求捞的博客-优快云博客

chnzbq的专栏 309

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

【多线程】初识多线程

多线程是操作系统调度的最小单位一个线程就是一个“执行流”,每个线程都可以按照顺序执行自己的代码,而多个线程之间则可以“同时”执行多份代码(实际为时间片轮转或并行,利用多核CPU实现)。一个进程下可以包含多个线程,而进程本身则对应一组进程控制块(PCB,Process Control Block),用于管理进程的资源和状态。进程打开任务管理器我们可以看到,这些列出的程序就是现在内存上正在运行着的进程随着计算机硬件的发展,并发编程已经成为刚需。

monsteraw的博客 1303

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

多线程——初识

单线程     1) 当一个线程正在写入数据时,其他线程不能写,也不能读。     2) 当一个线程正在读入数据时,其他线程不能写,但能够读。     备注     我们不能让两个或两个以上的用户同时更新同一条记录,如果两个或两个以上的用户都试图同时修改同一记录,那么该记录中的信息就会被破坏。     我们也不让一个用户更新数据库记录的同时,让另一用户读取记录的内容。因为读取

王萌萌 1636
上一篇: Annual salary30W+Java开发工程师——3.数组
下一篇: Java中线程的状态
bit_zhy
博客等级 码龄5年 370粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值