线程的认识

线程的引入

上一篇中,我们主要讲到了进程,多任务操作系统,希望系统能同时运行多个程序.如果是单任务的操作系统,完全不涉及进程,也不需要管理,更不需要调度.因此,本质上来说,进程是用来解决"并发编程"这样的问题的.但在一些特定的情况下,进程的表现,往往不尽如人意.比如,有些场景下,需要频繁的创建和销毁进程的时候,此时使用多进程编程,系统的开销就会很大.

对于服务器出现的问题

对于服务器来说,它同一时刻会收到很多请求,而针对每个请求,都会创建出一个进程,给这个请求提供一定的服务,返回对应的响应.一旦这个请求处理完了,此时这个进程就要销毁了.如果请求很多,意味着服务器就需要不停地创建新的进程,也不停销毁旧的进程.频繁地进行创建和释放,这样的操作,开销是比较大的.其中最关键的原因,就在于资源的申请与释放.因为进程是资源(CPU,硬盘,内存,网络带宽)分配的基本单位.

一个进程,刚刚启动的时候,首当其冲,就是内存资源.进程需要把依赖的代码和数据,从磁盘加载到内存中.而从系统分配一个内存,并非是一件容易的事情.一般来说,申请内存的时候,需要指定一个大小.系统内部就把各种大小的空闲内存,通过一定的数据结构,给组织起来了.实际申请的时候,就需要去这样的空间进行查找,找到大小合适的内存,分配过来.

结论:进程在进行创建和销毁时,开销大(主要体现在资源的申请与释放上).

线程,就是上述问题的解决方案.线程也可以称为一种轻量级的进程,在进程的基础上,做出了改进.下面就让我们走进线程,来看一看线程是如何解决这样的问题吧.

认识线程

概念

(1)线程是什么

一个线程就是一个执行流.每个线程之间都可以按照顺序执行自己的代码.多个线程之间"同时"执行多份代码.通俗地来讲,就是可以使一个代码的多处部分,同时进行.

因此,我们说线程保持了独立调度执行,这样的"并发支持",同时省去了"分配资源""释放资源"带来的额外开销.

(2)为啥要有线程

首先,"并发编程"成为"刚需"

单核CPU的发展遇到了瓶颈,要想提高算力,就需要多核CPU.而并发编程能更充分利用多核CPU资源.

有些任务场景需要"等待IO",为了让等待IO的时间能够去做一些其它的工作,也需要用到并发编程.

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

创建线程比创建进程更快.

销毁线程比销毁进程更快.

调度线程比调度进程更快.

前面介绍了会使用PCB来描述一个进程.现在,也使用PCB来描述一个线程.来看下面这张图:

先来说一个概念:进程是包含线程的,.

在图一中,是一个进程有一个PCB,但是实际上,一个进程可以有多个PCB,意味着这个线程包含了一个线程组(多个线程).操作系统,进行"多任务调度",本质上是在调度PCB(线程在系统中的调度规则,就和之前的进程是一样的),线程中的PCB中也有状态,优先级,上下文,记账信息.

PCB中,有个属性,是内存指针.多个线程的PCB的内存指针,指向的是同一个内存空间.这样就意味着,只是创建第一个线程的时候需要从系统分配资源.后续的线程,就不必分配.直接共用前面的那份资源就可以了.除了内存以外,文件操作符表(操作硬盘)这个也是多个线程共用一份的.

(3)进程与线程的区别

1.进程是包含线程的.每个进程至少有一个线程存在,即主线程

2.每个线程也是独立的执行流,可以执行一些代码,并单独参与到CPU调度中(状态,上下文,优先级,记账信息)每个线程都有一份.

3.进程和进程之间不共享资源,同一个进程和线程之间共享一个内存空间(文件操作符表)

4.根据2,3可得:进程是资源分配的基本单位,线程是调度的基本单位.

5.进程与进程间,不会相互影响,如果某个进程中某个线程抛出异常,可能会影响到其它线程

6.同一个进程中的线程间,可能会互相干扰,引起线程安全问题.

7.线程也不是越多越好,要足够合适,如果线程太多了,调度开销可能会十分明显

(4)Java线程和操作系统中的线程之间的关系

线程是操作系统中的概念,操作系统内核实现了线程这样的机制,并且对用户层提供了一些API供用户使用(如Linux的pthread库)

Java标准库中Thread类可以视为是对操作系统提供api进行了进一步的抽象与封装.

第一个多线程程序

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

每个线程都是一个独立的执行流,多个线程之间是"并发"执行的.

import static java.lang.Thread.sleep;

class MyThread extends Thread {
    @Override
    //run方法是线程的入口方法(不需要程序员手动调用,会在合适时机(线程创建好之后),被jvm自动调用执行)
    public void run() {
        while(true) {
            System.out.println("run方法中的线程执行....");

            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        //start()方法用于启动线程
        t.start();

        while(true) {
            System.out.println("main方法中的线程执行....");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

通过打印的方式可以清晰地看到两个执行流的执行逻辑(线程的执行逻辑),那么有什么方法能更清晰地看到两个线程的执行呢?这就用到了jdk中的jconsole工具. 

在你已经运行了程序之后,可以进来看到线程的情况

 创建线程

方法1:继承Thread类

继承Thread来创建一个线程类.

//因为Thread是在java.lang路径下的,因此不用导包
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
    }
}

创建MyThread类的实例

MyThread t = new MyThread();

调动start方法启动线程(本质上是会调用系统的api,来完成创建线程操作)

t.start();  //线程开始运行

 方法2:实现Runnable接口

1.实现Runnable接口

/*
Runnable可理解成"可执行的",通过该接口,
就可抽象表示出一段可被其它实体来执行的代码
*/
class MyRunnable implements Runnable {
    //runnable要表示的这段代码
    @Override
    public void run(){
        System.out.println("这里是线程执行的代码");
    }
}

2.创建Thread类实例,调用Thread的构造方法时将Runnable对象作为target参数.

//搭配Thread类以创建真正的线程
Thread t = new Thread(new MyRunnable());

3.调用start方法

t.start();    //线程开始运行

对比上面的两种方法:

 1.继承Thread类,直接使用this就可以表示当前线程对象的引用.

2.实现Runnable接口,this表示的是MyRunnable的引用.需要使用Thread.currentThread()

(后面会介绍)

方法3:继承Thread,重写run,但是使用匿名内部类

//使用匿名内部类创建Thread子类对象
Thread t1 = new Thread() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建Thread子类对象");
    }
}

 简要回顾一下匿名内部类这个知识点:匿名内部类是内部类,即在一个类里面定义的类.由于它是匿名的,所以就意味着不能重复使用,用完一次就扔了.

在Thread t1 = new Thread() {...};中,写{}的意思是要定义一个新的类.与此同时,这个新的类,继承自Thread.此处{}中可以定义子类的属性和方法.此处最重要的目的就是重写run方法.

于此同时,这个代码还创建了子类的实例.因此,t1指向的实例,并非是单纯的Thread,而是Thread的子类(但是我们不知道这个子类叫啥,因为它是匿名的).

 方法4:匿名内部类创建Runnable子类对象

//使用匿名内部类创建Runnable子类对象
Thread t2 = new Thread(new Runnable() {
    //Thread构造方法的参数,填写了Runnable的匿名内部类实例
    @Override 
    public void run() {
        System.out.println("使用匿名类创建Runnable子类对象");
    }
});

 方法五:lambda表达式创建Runnable子类对象(常用/推荐)

//这里()是形参列表,这里能带参数,但线程的入口不需要参数
//比如使用lambda代替Comparator,可以带上两个参数
//()前面应该有一个函数名,但此处作为匿名函数,就没有名字了
Thread t = new Thread(() -> {
    System.out.println("hello thread");
});

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VTK物体表面画贴合线条

1、自由画线 2、曲线拟合画线 3、三点闭合曲线

软件流程设计之事件风暴EventStorming

最近新开了一个公众号,有兴趣可以关注一下。时不时就复活去更新一下。 最近在带几个新员工,新员工是学校刚毕业的,习惯于做一些导师或者师兄师姐们拆解好的任务,有很明确的功能描述,甚至喂饭喂到什么地步呢&#xff0…

跨境电商的微商业务:个人品牌的成功之路

随着互联网的发展,跨境电商行业迅猛崛起,微商业务作为其中的一种新型销售模式也逐渐崭露头角。微商业务以低成本、高灵活性的特点,为个人品牌的成功之路开辟了新的可能性。 本文将深入研究跨境电商微商业务,探讨在这个领域中个人…

竞赛YOLOv7 目标检测网络解读

文章目录 0 前言1 yolov7的整体结构2 关键点 - backbone关键点 - head3 训练4 使用效果5 最后 0 前言 世界变化太快,YOLOv6还没用熟YOLOv7就来了,如果有同学的毕设项目想用上最新的技术,不妨看看学长的这篇文章,学长带大家简单的…

GitHub上8个强烈推荐的 Python 项目

文章目录 前言1. Manim2. DeepFaceLab3. Airflow4. GPT-25. XSStrike6. 谷歌图片下载7. Gensim8. SocialMapper总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③…

Vue3-provide和inject

作用和场景:顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信 跨层传递普通数据: 1.顶层组件通过provide函数提供数据 2.底层组件通过inject函数获取数据 既可以传递普通数据,也可以使用ref传递响应式数据&#xff08…

WorkPlus即时通讯,打通上下游产业链,构建企业生态圈

如今,随着信息技术的迅速发展,智慧水务、智慧医疗、智慧城市、智慧教育、智慧政务等领域正蓬勃发展。在这个智慧时代,企业需要一个具备开放性和扩展性的平台级产品,以满足多样化的业务需求。WorkPlus作为一款全新的移动底座产品&a…

Linux应用开发基础知识——I2C应用编程(十二)

前言: I2C(Inter-Integrated Circuit BUS)是集成电路总线,是目前应用最广泛的总线之一,最初由PHILIPS(现为NXP)设计。它使用多主从架构,主要用于连接低速周边设备。I2C总线在硬件物理…

高效聚合 | AIRIOT智慧虚拟电厂管理解决方案

传统的电力供应模式主要依靠大型发电厂和电网进行能源传输和分配,但这种模式会导致能源浪费、环境污染等问题,往往存在如下的运维问题和管理痛点: 资源整合能力差:传统电力供应模式无法集成和整合分散的电力资源,包括…

将所有图片居中对齐

Ctrl h 调出替换框 ^g表示所有图片 格式里面选择段落 全部替换

国自然项目基金撰写的隐藏技巧、范例分析及提交前的自我审查

目录 一、基金项目申请要求、重点及项目介绍 二、基金的撰写技巧 三、基金撰写的隐藏技巧 四、范例分析及提交前的自我审查 更多应用 基金项目申请需要进行跨学科的技术融合,申请人需要与不同领域结合,形成多学科交叉的研究。基金项目申请在新时期更…

JS逆向之wasm逆向(二)

本文仅供技术交流和技术学习 不做其他用途 接着上一篇继续讲: 上篇地址: JS逆向之wasm逆向(二进制) 网址: aHR0cHM6Ly93d3cuN3E2Y3lqLmNvbTo5MDAxL3JlZ2lzdGVyNDY5Njg/aV9jb2RlPTQ0Mjc5OTU1 这个网站我们后面可以继续讲他的debugger 和滑块…

放弃无谓的「技术氛围」幻想,准备战斗

大型科技公司每年都招聘大量研发人才,这给了很多人一种错觉,认为是「技术」导致了这些公司的成功,其实他们的成功是技术推动的市场战略的成功,是市场需要某项服务,才需要研发人员夜以继日的埋头苦干。资本绝不会做亏本…

前端设计问题:iframe

居中问题&#xff1a; 尝试了一般的居中方法&#xff0c;无效果 display: flex;justify-content: center;align-items: center;放到导航栏下面不居中 放到页面底部还是不居中 Code <iframe id"demo_sanshui" src"demo_sanshui.html" width"120%…

浅析基于智能音视频技术的城市重要场馆智能监控系统设计

了解旭帆科技的朋友都知道&#xff0c;旭帆科技一直都乐于和大家分享各类场景的视频解决方案&#xff0c;今天小编就基于智能音视频技术的城市重要场馆智能监控系统设计和大家探讨一下。 基于智能音视频技术的城市重要场馆智能监控系统设计&#xff0c;主要包含以下要素&#x…

java项目之消防物资存储系统(ssm+vue)

项目简介 消防物资存储系统实现了以下功能&#xff1a; 管理员功能: 管理员登陆后&#xff0c;主要模块包括首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;仓库管理&#xff0c;物资入库管理&#xff0c;物资出库管理&#xff0c;仓库管理&#xff0c;物资详情管…

【C语言:深入理解指针二】

文章目录 1. 二级指针2. 指针数组3. 字符指针变量4. 数组指针变量5. 二维数组传参的本质6. 函数指针变量7. 函数指针数组8. 转移表9. 回调函数10. qsort函数的使用与模拟实现 1. 二级指针 我们知道&#xff0c;指针变量也是变量&#xff0c;它也有自己的地址&#xff0c;使用什…

微信小程序中使用腾讯地图bindmarkertap=“onMarkerTap“事件无法获取数据的id解决方法

问题描述 由于id是字符串类型&#xff0c;导致获取不到id 点击tap后&#xff0c;获取不到我们数据中的id 解决方法 只需要把id转成Number类型即可 点击tap后&#xff0c;即可获取到我们数据中对应的id

什么是复费率电表?

随着科技的不断进步和人们对能源管理的日益重视&#xff0c;复费率电表逐渐成为我国电力系统中不可或缺的一员。复费率电表是一种能够实现电能计量、峰谷电价划分以及负荷控制等多功能的智能电表&#xff0c;它采用先进的通信技术和计算机算法&#xff0c;对用户的用电行为进行…

JOSEF约瑟 过电流继电器 JL15-300/11 触点形式一开一闭 板前接线

系列型号 JL15-1.5/11电流继电器JL15-2.5/11电流继电器 JL15-5/11电流继电器JL15-10/11电流继电器 JL15-15/11电流继电器JL15-20/11电流继电器 JL15-30/11电流继电器JL15-40/11电流继电器 JL15-60/11电流继电器JL15-80/11电流继电器 JL15-100/11电流继电器JL15-150/11电流继电…