【多线程】线程(线程的概念+线程的创建)-程序员宅基地

技术标签: jvm  面试  线程的概念  java  多线程  线程创建  

线程

C++的生态鼓励多进程编程,而在Java的生态中,更鼓励多线程编程

一、线程的概念

1.引入线程的目的

​ 引入多进程的目的,就是为了实现并发编程。但是进程太“重”,导致开销大、效率低。创建、销毁或者调度一个进程,消耗的时间都较多。主要消耗在申请资源上面。

​ 前文提到,进程是系统资源分配的基本单位。分配的消耗很大。

​ 例如分配内存操作:操作系统内部的数据结构,会把空闲的内存分块管理好。当我们进行申请内存时,操作系统就会从这个数据结构中,找到一块大小合适的空闲内存,返回给对应的进程。虽然数据结构会提高效率,但是由于要管理的内存很多,相比之下依然耗时。

如果进行频繁的创建和销毁进程,这时开销就不容忽视。

为了解决这个问题,就引入了线程(Thread)。

2.什么叫线程(Thread)

  • 线程也叫”轻量级进程“,创建、销毁、调度线程要比进程要快。

  • 线程依附于进程,不能独立存在。进程包含一个/多个线程。

  • 线程是调度执行的基本单位。

一个进程可以用一个或多个PCB来表示,每个PCB对应到一个线程。

每一个线程,都有状态、优先级、上下文、记账信息等等来辅助调度。

pid、内存指针、文件描述符表是共用的。

1.线程的特点:

1.每个线程都可以独立的在CPU上进行调度。

2.同一个进程的多个线程之间,共用同一分内存空间和文件资源。

  • 因此在创建线程时,不需要重新申请资源,直接复用之前分配给进程的资源。省去了资源分配的开销,提高了效率

3.当线程过多,可能对引发冲突。(两个线程同时访问一个变量:线程不安全)。当一个线程发生异常时,如果没有妥善处理,容易奔溃整个进程。

2.进程和线程的区别(面试题):

1.进程包含线程,一个进程可以有一个线程或者多个线程。在开始时至少要有一个线程存在(主线程)——是和进程一起创建的

这个线程负责完成执行代码的操作,也可以根据需求,创建出多个线程。每个线程都可以独立执行不同的代码,从而实现并发编程的效果。

2.进程是系统资源分配的最小单位,线程是系统调度执行的最小单位。

3.进程和线程都是为了实现并发编程的需求,但是线程比进程更加高效

4.进程和进程之间不共享内存空间,同一个进程的线程之间共享同一份内存空间和文件资源,省去了申请资源的开销。

5.进程之间具有独立性,一个进程出问题,不影响其他进程,不相互干扰。用一个进程的不同线程之间,可能会相互影响

3.Java的多线程编程

线程是操作系统中的概念,操作系统提供了对应的API,来对线程进行操作。

为了跨平台,Java又对系统的API进行了封装(Thread类),通过Thread类创建Thread对象,从而操作系统内部的进程。

二、创建线程

1.继承Thread重写run

写一个类继承自Thread

Thread类是Java标准库内置的类,在java.long包的下面,不需要import

class MyThread extends Thread{
    
    //Java标准库内置的类,在java.long包的下面,不需要import
    @Override
    public void run() {
    
        //重写父类的run方法(入口方法)
        System.out.println("hello thread");
    }
}
入口方法:

每个线程都是一个独立的执行流,都可以独立执行代码。当一个线程跑起来时,从它的入口方法开始执行代码。

当运行一个Java程序时,就是跑起来了一个Java进程。这个进程里至少存在一个线程(主线程),这个主线程的入口方法就是main方法。

定义好线程后,需要对线程进行调用。

    public static void main(String[] args) {
    
        //在主线程中创建实例
        Thread t = new MyThread();
        //start和run都是Thread的方法
        //run 只是描述了线程的入口(线程要做什么任务)---指路的
        //start 真正调用了系统API,在系统中创建出这个线程,让这个线程再调用run
        t.start();

    }

run 只是描述了线程的入口(线程要做什么任务)

相当于一个psvm,里面是要执行的内容。

start 真正调用了系统API,在系统中创建出这个线程,让这个线程再调用run

相当于手动的绿色运行小三角,有了start才能开始创建、运行线程

class MyThread extends Thread{
    
    //Java标准库内置的类,在java.long包的下面,不需要import
    @Override
    public void run() {
    
        //重写父类的run方法(入口方法)
       while (true){
    
           System.out.println("hello thread");
       }
    }
}
public class CreatThread {
    
    public static void main(String[] args) {
    

        Thread t = new MyThread();
        t.start();
        while (true){
    
            System.out.println("hello Main");
        }
    }
}
  • 此时,主线程和t线程的两个死循环在“同时执行”。日志在交替打印

在这里插入图片描述

​ 验证了每一个线程都是一个独立的执行流,在主线程中,遇到t线程时,兵分两路。一路去执行t进程,另一路继续执行主线程。实现了并发执行。

    public static void main(String[] args) {
    

        Thread t = new MyThread();
        //t.start();
        t.run();
        while (true){
    
            System.out.println("hello Main");
        }
    }

​ 如果把start()给成run()。将不会创建出新的线程,只有一个主线程。在主线程中执行这个对象的方法,并且死循环,不会进入到下面的循环中。

使用jconsole

使用 jconsole来观察进程中多线程的情况。

JDK中自带的工具

在JDK的目录下 : bin/jconsole.exe

  • 启动时要保证线程已经运行。

在这里插入图片描述

一个Java进程启动后,JVM会自动创建线程来进行一系列操作(如垃圾回收、资源统计、远程方法调用等等)

sleep
  • sleep是Thread的类方法,通过类名直接调用
  • 在使用sleep时,可能会抛出受查异常,必须显示处理
       while (true){
    
           System.out.println("hello thread");
           try {
    
               Thread.sleep(1000);
               //每执行一次,休眠一秒
           } catch (InterruptedException e) {
    
               throw new RuntimeException(e);
           }
       }

在循环体中加sleep,进行休眠。

两个线程都休眠1000毫秒,时间到了之后,谁先执行是“随机”的。操作系统对于多个线程的调度顺序是”随机“的。

概率不均等,取决于调度器的具体实现

2.实现Runnable重写run

Runnable是一个interface接口

Runnable表示的是一个“可以运行的任务”,这个任务是交给线程负责执行,还是交给其他的实体执行。Runnable本身并不关心。

class MyRunnable implements Runnable{
    

    @Override
    public void run() {
    
       while (true){
    
           System.out.println("hello thread");
           try {
    
               Thread.sleep(1000);
           } catch (InterruptedException e) {
    
               throw new RuntimeException(e);
           }
       }
    }
}
public class demo1 {
    
    public static void main(String[] args) throws InterruptedException {
    
        Runnable runnable = new MyRunnable();
        //将任务放到线程里面执行
        Thread t = new Thread(runnable);
        t.start();
        //还是通过start来创建线程
        while (true){
    
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }

使用继承Thread写法和实现Runnable接口写法的区别,主要是解耦合

创建一个线程,需要进行两个关键操作:

1.明确线程要执行的任务

任务本身不一定和线程强相关,把任务提取出来,随时对代码进行修改。 达到解耦合的效果。

2.调用系统api来创建出线程

3.使用匿名内部类创建

    public static void main(String[] args) throws InterruptedException {
    

        Thread t = new Thread(new Runnable() {
    
            @Override
            public void run() {
    
                while (true){
    
                    System.out.println("hello thread");
                    try {
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t.start();
        while (true){
    
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
public static void main(String[] args) throws InterruptedException {
    
        Thread t = new Thread(new Runnable() {
    
            @Override
            public void run() {
    
                while (true) {
    
                    System.out.println("hello thread");
                    try {
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t.start();
        while (true) {
    
            System.out.println("hello main");
            Thread.sleep(1000);
        }

    }

4.使用lambda表达式创建

    public static void main(String[] args) throws InterruptedException {
    
        Thread t = new Thread(()->{
    

            while (true){
    
                System.out.println("hello thread!");
                try {
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        while (true){
    
            System.out.println("hello Main");
            Thread.sleep(1000);
        }
    }

点击移步博客主页,欢迎光临~

偷cyk的图

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_64003319/article/details/137568293

智能推荐

操作系统中典型的调度算法_非剥夺式优先级调度算法-程序员宅基地

文章浏览阅读2.6k次。操作系统 调度算法_非剥夺式优先级调度算法

vue中 同步,异步获取后台数据并在另外的方法中调用该数据_vue中success的结果传出-程序员宅基地

文章浏览阅读1.2k次。【代码】vue中 同步,异步获取后台数据并在另外的方法中调用该数据。_vue中success的结果传出

Web入门之VScode连接数据库sql server(超详细)_vscode连接sql server-程序员宅基地

文章浏览阅读2.5w次,点赞53次,收藏292次。Web入门之VScode连接数据库sql server(超详细)今天我们终于开始连接数据库啦,作为一个登录页面,怎么能不连接我们已经建立好的school数据库呢,下面,我们一起来连接吧,非常简单哦。打开数据库第一步当然就是打开我们的数据库啦,以前我们可能常常使用本机地址一键登录,不过这次我们需要使用密码登录喽。身份验证这里我们选择SQL Server 身份验证,登录名一般默认都是sa,输入密码,密码我们之前设好过,如果忘记也没关系,是可以修改的,如果忘记的话,之后我会写一篇文章,介绍如何修改密码。_vscode连接sql server

性能监控系统的搭建(转)-程序员宅基地

文章浏览阅读253次。引言前阵子在w3ctech的走进名企 - 百度前端 FEX 专场上曾“夸下海口”说听完讲座后七天就可以打造自己的前端性能监控系统,既然说出去了也不能食言。从前一篇文章前端数据之美相信大家对前端数据有了一定的了解,下面就针对其中的性能数据及其监控进行详细阐述。开始行动本文中的性能主要指 web 页面加载性能,对性能还不了解?不用担心,接下来的“每一天”跟我一起进入前端性能的世界。Da..._用ssm搭建远程服务器性能监控系统

npm使用国内淘宝镜像(最新地址)_npm最新淘宝镜像-程序员宅基地

文章浏览阅读5.1w次,点赞143次,收藏203次。我们前端程序员在使用使用国外的镜像源速度很慢并且容易下载失败,有时候需要尝试多次才有可能下载成功,很麻烦,但是可以切换为国内镜像源,下面我介绍下如何使用淘宝镜像源。通过上述的操作,我们可以轻松地配置和使用淘宝镜像来加速npm包的下载、安装和更新。_npm最新淘宝镜像

hive case when的选择顺序优先级问题_hive case when then-程序员宅基地

文章浏览阅读9.1k次,点赞6次,收藏9次。hive 中有case when 的语法是:case when 条件1 then 结果1when 条件2 then 结果2when 条件3 then 结果3......else 结果x end那如果被查询的行同时符合条件1和条件3呢?结果会是出现“结果1”还是“结果3”呢?根据测试,是符合结果1,原因是语句先“碰见” when 条件1 then 结果1这一句。如果语句改为:se..._hive case when then

随便推点

多账号自媒体工具,多平台同时发布_多ip 多账号 多平台 文章自动发布 开源-程序员宅基地

文章浏览阅读699次。现在使用自媒体工具进行头条号内容发布的自媒体人有很多,尤其是头条号作为自媒体主流平台,在各大平台里,所占的人数也具有一定的数量,很多人不是只运营头条号一个平台,其他的平台也有在运营。由于平台账号的增加,大多数人在内容分发上面花费的时间越来越多,这个时候大家就可以用到蚁小二自媒体工具,5分钟就可以把内容发布到多个平台,还能批量导入上百个账号,检测文章原创度。同时在给大家分享几个我们在自媒体运营中需要用到的小工具。1.找素材工具关于素材方面的工具,大家应该也见过很多,像易撰素材采集工具这.._多ip 多账号 多平台 文章自动发布 开源

分布式文件存储系统minio、大文件分片传输_miniio 分片写入文件-程序员宅基地

文章浏览阅读991次。MD5计算将整个文件或者字符串,通过其不可逆的字符串变换计算,产生文件或字符串的MD5散列值。如果传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。因为如果文件、字符串的MD5散列值不一样,说明文件内容也是不一样的。包含了一套完整的事件模型,用于捕获读取文件时的状态,下面这个表格归纳了这些事件。通过slice方法,从blob1中创建出一个新的blob对象,size等于3。的一个下标,这个下标-1的对应的字节将会是被拷贝进新的。,其中 3 个用以读取文件,另一个用来中断读取。_miniio 分片写入文件

华为OD机试真题2023(JAVA)_华为机试-程序员宅基地

文章浏览阅读1.1w次,点赞53次,收藏214次。在华为od岗的薪资问题,并没有比市场上多多少,具体根据个人职级和绩效而定,初入职场的应届生薪资大概在20K-30K左右,相对于其他行业来说,华为的起薪是比较高的。华为OD机试通常由多个题目组成,包括算法设计、编程、调试等多个环节,考察候选人的基础知识、实际能力和算法编写能力等多个方面,是华为招聘流程中非常重要的一环。大家有什么不懂的,想看的,都可以留言,或者私信哪吒。总的来说,OD岗位的晋升空间相对较广,但是需要候选人不断提升自身能力,积极参与公司的内部培训和发展计划,才能在职业生涯中获得更好的发展机会。_华为机试

html中两个块之间有空隙,css标签之间引发空隙问题解决办法-程序员宅基地

文章浏览阅读1.2k次。首先看这段空隙对应的源代码如下代码看起来没有任何问题,那么问题真的来了:空隙去哪里了啊?问题原因:图片文字等inline元素默认是和父级元素的baseline对齐的,而baseline又和父级底边有一定距离(这个距离和font-size,font-family相关,不一定是5px),所以设置vertical-align:top/bottom/text-top/text-bottom都可以..._html父元素和子元素间始终有空格

JTable冻结列的简单实现_用jtable实现excel冻结列-程序员宅基地

文章浏览阅读2.8k次。JTable实现冻结列的示例_用jtable实现excel冻结列

highlight.js网页代码高亮插件左侧怎么展示行号_highlightjs/vue-plugin 行号-程序员宅基地

文章浏览阅读288次。前几天分享了一篇网页代码高亮插件highlight.js,效果还不错,因为highlight.js本身没有行号展示,对于观感和体验上来源还是有点不太友好哈,恩。这几天越看越难受,忍不了还是弄一下吧。操作完成,上面代码框展示的就是配置后的效果了,因为我不大喜欢侧那块灰色的条,所以在css里面注释了,因人而异吧,喜欢的可以自己打开试下效果。_highlightjs/vue-plugin 行号

推荐文章

热门文章

相关标签