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

技术标签: 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

智能推荐

latex h t b p是什么意思_latex htpb-程序员宅基地

文章浏览阅读1.4w次,点赞9次,收藏17次。常用选项[htbp]是浮动格式:『h』当前位置。将图形放置在正文文本中给出该图形环境的地方。如果本页所剩的页面不够,这一参数将不起作用。『t』顶部。将图形放置在页面的顶部。『b』底部。将图形放置在页面的底部。『p』浮动页。将图形放置在一只允许有浮动对象的页面上。在table或者figure 后加 [!htb] 是系统忽略“美学”标准,把表格和图片插入到你的代码中,是动的,但是不加感叹号,它就是按顺序选择h(此处),t(上方),b(下方),所以为了让图片随着你的代码移动,最好加一个[!htb]_latex htpb

【转载】linux下的usb抓包方法-程序员宅基地

文章浏览阅读67次。1 linux下的usb抓包方法1、配置内核使能usb monitor:make menuconfigDevice Drivers --> USB Support --> USB Monitor --> Sel..._linux安装tcpdump 查看usb

计算机组成pc em ir,计算机组成 课程设计报告.doc-程序员宅基地

文章浏览阅读164次。计算机组成 课程设计报告计算机组成原理课程设计报告姓 名:班 级:学 号:指导老师:2016年 6月31日目 录第一章 背景知识与课设任务概述11.1课设目的11.2课设任务11.2111.2211.2321.2421.252第二章 课设内容32.1指令的执行流程32.1.132.1.242.1.352.2存储器62.2.162.3运算器72.3.172.4硬件系统组成122.4..._计算机组成课程设计报告

python青果教务系统抢课_名额不够,技术来凑,利用Python实现教务系统强制性抢课...-程序员宅基地

文章浏览阅读1.3k次。最近一学期一次的抢课大戏又来了,几家欢乐几家愁。O(∩_∩)O哈哈~(l我每次一选就过了hah,我还是有欧的时候滴)。看着他们盯着教务系统就着急,何况我们那教务系统,不想说什么。emmm 想周围的朋友,正好下午利用扩容前一段时间写了个小脚本帮助朋友抢课。(当然抢到了啦,^_^)私信小编001即可获取大量Python学习资料,名额有限因为时间不够,来不及仔细琢磨,我第一想法就是直接提交选课的数据包(..._青果教务系统抢课

windows 加 switchyomega + burp 抓https包-程序员宅基地

文章浏览阅读4.6k次。很简单,下载证书后导入到受信任根目录证书下载,直接在代理状态浏览器访问burp点击CA就可以下载了 设置该证书全部信任,,switchyomega 设置如下即可 就可以抓https的包了 ...

用C语言写循环赛日程表,循环赛的方法与编排-程序员宅基地

文章浏览阅读1k次。一、循环赛的种类与特点(一)循环赛的种类循环赛又称循环法。是指参赛队(或个人,下同)之间,都要互相轮流比赛,最后按照各参赛队在全部比赛中的胜负场数、得分多少排定名次的比赛方法。它在对抗性项目比赛中经常被采用。循环赛包括单循环、双循环或分组循环三种。单循环是所有参赛队(人)相互轮赛一次;双循环是所有参赛队(人)相互轮赛二次;分组循环是参赛队(人)较多时,采用种子法,把强队(人)分散在各组,先进行小组..._c语言循环赛互打一场比赛 甲队两胜

随便推点

507页XX市应急管理局智慧矿山煤矿数字化矿山技术解决方案_18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案word-程序员宅基地

文章浏览阅读542次。只有在单系统自动化的基础上,通过高速网络接入各单系统,充分数据融合,建立合理的联动机制才能完成从单系统自动化到综合自动化的转变,该部分的转变从投入的资金和实现的容易度相对来讲可实现性和可控性都比较容易,但是从综合自动化向数字化矿山发展,涉及的面比较广,必须由多方共同来推进,一般涉及到“综合自动化”、“空间数字化”及“管理信息化”三大方面,三者缺一不可,通过三者的有机融合,再通过合适的平台例如三维可视化平台进行展示,同时通过科学合理的管理制度和流程加以应用才是真正意义上有血有肉的数字化矿山。_18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案word

Tomcat官网地址-程序员宅基地

文章浏览阅读1.4w次,点赞8次,收藏7次。Tomcat官网地址_tomcat官网

汇编指令长度计算_汇编指令占多少字节-程序员宅基地

文章浏览阅读5.1k次,点赞11次,收藏58次。指令长度与寻址方式有关系,规律或原则如下:一、没有操作数的指令,指令长度为1字节。如es:ds:cbwxlat等。二、操作数只涉及寄存器的指令,指令长度为2字节。如mov al,[si]mov ax,[bx+si]mov ds,ax等。三、操作数涉及内存地址的指令,指令长度为3字节。如mov al,[bx+1]mov ax,[bx+si+3]lea di,[1234]mov [2345],ax等。四、操作数涉及立即数的指令,指令长度为:寄存器类型+2。8位寄存器,寄存器_汇编指令占多少字节

二、RSA加密_ctf rsa 多个n和多个c-程序员宅基地

文章浏览阅读3.4k次。CTF中的RSA及攻击方法笔记1 数论基础1.1 模运算规则2 RSA相关题目2.1 已知 n,e,c 求 m2.2 已知 p,q,e 求 d2.3 已知dp,dq,c,p,q 求m2.4 仅已知c,c特别大 【c = m^e mod n】2.5 已知n1,n2,c1,c2,n 求 m2.6 已知n1,n2,e,c2 求m2.7 已知e,d,N 求p,q1 数论基础参考链接:https://www.freebuf.com/articles/web/257835.html1.1 模运算规则模运算与基_ctf rsa 多个n和多个c

mysql中把bigint类型转换为时间格式,与hive中unix_timestamp、FROM_UNIXTIME两个函数之间的区别_bigint转日期-程序员宅基地

文章浏览阅读2w次,点赞4次,收藏15次。数据库中时间类型是这样的,13位bigInt类型的数据select date_format(FROM_UNIXTIME(列名/1000),'%Y%m%d') from xx表原理就是把13位的时间格式/1000等于时间戳,使用FROM_UNIXTIME把时间戳转换成具体的日期ps:将时间转换为时间戳select unix_timestamp('2018-08-30..._bigint转日期

exit status 5: �ܾ����ʡ� exit status 1: ���_exit status 5: exit status 1:-程序员宅基地

文章浏览阅读1.1k次。使用nvm切换node版本出现上述乱码时。使用管理员模式打开CMD就可以解决了~_exit status 5: exit status 1:

推荐文章

热门文章

相关标签