【多线程】线程(线程的概念+线程的创建)

news2024/11/26 4:51:13

文章目录

  • 线程
    • 一、线程的概念
      • 1.引入线程的目的
      • 2.什么叫线程(Thread)
          • 1.线程的特点:
          • 2.进程和线程的区别(面试题):
          • 3.Java的多线程编程
    • 二、创建线程
      • 1.继承Thread重写run
          • 入口方法:
          • 使用jconsole
          • sleep
      • 2.实现Runnable重写run
      • 3.使用匿名内部类创建
      • 4.使用lambda表达式创建

线程

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的图

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

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

相关文章

文件夹变应用?数据恢复大解密!

在日常使用电脑的过程中,许多用户可能都曾遭遇过这样一个奇怪的现象:原本用来存放文件的普通文件夹,突然变成了应用程序的图标。这种突如其来的变化不仅令人困惑,更可能导致重要数据的丢失或损坏。那么,究竟是什么原因…

最佳UI设计软件推荐:全球顶尖工具一览!

在这个信息化、数字化的时代,我们的生活、工作,甚至娱乐,都被各种各样的网站所包围。随着技术的发展,人们对网页UI设计的要求也越来越高。所以问题是,作为一个想要提高他们的UI设计能力的设计师,你应该如何…

渗透学习第一天:DR4G0N B4LL靶场复现

0x00 环境搭建 攻击机为kali Linux,IP为192.168.71.129 靶机IP地址目前不知道,但是是和kali同网段的 0x01 信息收集 由于不知道目标的IP地址,这里我采用了arp scan对本机的整个网段进行扫描 发现目标IP为192.168.71.130。对目标IP进行端…

高校人事管理系统业务分析

目标用户 大学人事部门,其他部门、院系、个人 解决问题 人事部门按业务划分了很多科室、数据分散、工作流程杂乱、工作效率低。 主要功能模块 人事综合管理平台、个人自助服务平台、人才招聘管理系统、薪酬管理子系统、职称评审子系统、绩效考核子系统组成。

【精选】发布应用到应用商店的基本介绍

摘要 本文旨在介绍如何在各大应用商店发布应用,包括市场选择、准备材料、上架步骤以及常见被拒原因及解决方法。通过详细的步骤和经验分享,帮助开发者顺利将应用推向市场。 引言 随着移动应用市场的不断发展,越来越多的开发者希望将他们的…

44-技术演进(下):软件架构和应用生命周期技术演进之路

应用、系统资源、应用生命周期管理这 3 个维度,构成了我们对云的所有诉求。 我会介绍下应用维度和应用生命周期管理维度的技术演进。 我们就先来看下软件架构的演进之路。 软件架构的演进 软件架构技术演进如下图所示: 单体架构 在单体架构中&#xff…

c++——sort()函数

一、代码和效果 #include<bits/stdc.h> using namespace std;int main() {int a[6]{1,45,2,5,456,7};sort(a,a6);for(int i0; i<6; i){cout<<a[i]<<" "<<endl;}return 0; } 二、sort函数解析 &#xff08;从小到大&#xff09; std::so…

Linux安装MuJoCo各版本及D4RL教程

Linux安装MuJoCo各版本及D4RL教程 文章目录 Linux安装MuJoCo各版本及D4RL教程Linux安装MuJoco150一、下载MuJoco1501.1 文件下载1.2 文件存放位置1.3 环境变量设置 二、安装mujoco-py三、验证mujoco-py安装是否成功 Linux安装MuJoco200一、下载MuJoco2001.1 文件下载1.2 文件存…

【随笔】Git 高级篇 -- 快速定位分支 ^|~(二十三)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

php:实现压缩文件上传、解压、文件更名、压缩包删除功能

效果图 1.上传文件 2.压缩包文件 3.itemno1文件 4.上传到系统路径\ItemNo 更名后的itemno1文件 代码 <form action"<?php echo htmlspecialchars($_SERVER[PHP_SELF], ENT_QUOTES, UTF-8); ?>" method"post" enctype"multipart/form-dat…

Python+Selenium+Unittest 之Unittest3(TestSuite()和TextTestRunner())

目录 1&#xff1a;addTest() 2、addTests() 3&#xff1a;discover() 上一篇说了Unittest的一个基本的执行顺序&#xff0c;那如果我们想要调整用例的执行先后顺序的话&#xff0c;可以用TestSuite()和TextTestRunner()了&#xff0c;可以这么理解&#xff0c;比如一个班级…

TRON x HTX DAO 2024 香港之夜:共建香港元宇宙金融自由港

4月9日&#xff0c;由波场TRON主办&#xff0c;HTX DAO协办的“TRON x HTX DAO 2024 香港之夜”主题活动在香港盛大举行。多位参与HTX DAO生态建设的项目方代表、委员会成员、知名KOL等出席并就HTX DAO发展及加密业态进行演讲。 活动现场&#xff0c;波场TRON创始人孙宇晨通过视…

WordPress LayerSlider插件SQL注入漏洞复现(CVE-2024-2879)

0x01 产品简介 WordPress插件LayerSlider是一款可视化网页内容编辑器、图形设计软件和数字视觉效果应用程序,全球活跃安装量超过 1,000,000 次。 0x02 漏洞概述 WordPress LayerSlider插件版本7.9.11 – 7.10.0中,由于对用户提供的参数转义不充分以及缺少wpdb::prepare(),…

使用Docker部署开源项目FreeGPT35来免费调用ChatGPT3.5 API

Vercel部署FreeGPT35有严重限制&#xff0c;玩玩就好&#xff0c;真用还是得docker。 限制原因: Vercel的流式响应并不是一开始写流&#xff0c;客户端就能立刻收到响应流&#xff0c;而是先写到一个缓冲区&#xff0c;当流关闭才一股脑的流式响应回来(不是实时流) 因此导致: …

代码随想录Day31:贪心算法Part1

贪心算法的理论基础 主要的思路就是通过想局部最优解然后看能不能推导出全局最优&#xff0c;但是贪心算法没有统一的套路&#xff0c;每一个问题的贪心思路都可以非常不一样 Leetcode 455. 分发饼干 讲解前&#xff1a; 这时第一道贪心算法的题目&#xff0c;所以很简单&am…

Open CASCADE学习|放样建模

在CAD软件中&#xff0c;Loft&#xff08;放样&#xff09;功能则是用于创建三维实体或曲面的重要工具。通过选取两个或多个横截面&#xff0c;并沿这些横截面进行放样&#xff0c;可以生成复杂的三维模型。在CAD放样功能的操作中&#xff0c;用户可以选择不同的选项来定制放样…

【新手指南】创建简单搜索引擎优化报告

搜索引擎优化&#xff08;SEO&#xff09;是一项长期投资&#xff0c;需要持续监控&#xff0c;以确保产生您所期望的结果。这就是生成搜索引擎优化报告的重要性所在–这份报告会告诉你哪些有效&#xff0c;哪些无效&#xff0c;哪些需要改进。 如果你从未创建过搜索引擎优化报…

C语言 文件函数

目录 1. 文件的打开和关闭 2. 文件的顺序读写 2.1 顺序读写函数介绍 2.2读文件&#xff08;读文件只能读一次&#xff09; 2.3写文件 3. 文件的随机读写 3.1 fseek 3.2 ftell 3.3 rewind 4.文件读取结束的判定 4.1 被错误使误的 feof 我对读写的理解&#xff1a;(从…

SpringBoot 集成H2数据库,启动执行sql, 中文乱码

目录 H2数据库介绍 SpringBoot版本&#xff1a;SpringBoot 2.1.12.RELEASE 快速集成H2&#xff0c;maven依赖 快速集成H2&#xff0c;数据源及关键参数配置 spring.datasource.schema参数&#xff08;建表SQL脚本&#xff09; spring.datasource.data参数&#xff08;更新、…