【多线程】| 基本知识汇总

news2024/11/27 6:16:10

目录

  • 🦁 掌握基本概念
    • 1. 什么是线程?
    • 2. 什么是主线程以及子线程?
    • 3. 什么是串行?什么是并行? 什么是并发?
  • 🦁 线程的创建
    • 1. 通过继承Thread类实现多线程
    • 2. 通过Runnable接口实现多线程
  • 🦁 线程执行流程
  • 🦁 常用的方法
    • 1. 线程休眠
    • 2. 线程让步
    • 3. 线程联合
    • 4. 获取线程名称
    • 5. 修改线程名称
    • 6. 判断线程是否存活
  • 🦁 最后

🦁 掌握基本概念

1. 什么是线程?

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
在这里插入图片描述

在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)

2. 什么是主线程以及子线程?

在这里插入图片描述
当Java程序启动时,一个线程会立刻运行,该线程通常叫做程序的主线程(main thread),即main方法对应的线程,它是程序开始时就执行的。
在主线程中创建并启动的线程,一般称之为子线程。

3. 什么是串行?什么是并行? 什么是并发?

  1. 串行(serial):一个CPU上,按顺序完成多个任务(先完成一个再接着下一个,类似栈里面的过程)

在这里插入图片描述

  1. 并行(parallelism):指的是任务数小于等于cpu核数,即任务真的是一起执行的

在这里插入图片描述

  1. 并发(concurrency):一个CPU采用时间片管理方式交替的处理多个任务。一般是是任务数多于cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
    在这里插入图片描述

🦁 线程的创建

一般两种方法:一个是继承Thread类(单继承)、一个是实现Runnerable接口(多实现)。

1. 通过继承Thread类实现多线程

  • 在Java中负责实现线程功能的类是java.lang.Thread 类。
  • 可以通过创建 Thread的实例来创建新的线程。
  • 每个线程都是通过某个特定的Thread对象所对应的方法run( )来完成其操作的方法run( )称为线程体
  • 通过调用Thread类的start()方法来启动一个线程。
public class TestThread extends Thread { //自定义类继承Thread类
  //run()方法里是线程体
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(this.getName() + ":" + i);	//getName()方法是返回线程名称
        }
    }

    public static void main(String[] args) {
        TestThread thread1 = new TestThread();//创建线程对象
        thread1.start();//启动线程
        TestThread thread2 = new TestThread();
        thread2.start();
    }
}

2. 通过Runnable接口实现多线程

public class TestThread2 implements Runnable { //自定义类实现Runnable接口;
  //run()方法里是线程体;
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
    public static void main(String[] args) {
    //创建线程对象,把实现了Runnable接口的对象作为参数传入;
        Thread thread1 = new Thread(new TestThread2());
        thread1.start();//启动线程;
        Thread thread2 = new Thread(new TestThread2());
        thread2.start();
    }
}

🦁 线程执行流程

一个线程的一生需要经历五个状态:

开始——>就绪——>运行——>阻塞——>死亡

image-20230519143129078

详细变化如下👇


image-20230519143856461

  1. 新生态:在线程被new出来之后,拥有自己的内存空间则变成新生态。
  2. 就绪状态:等待被cpu执行的状态,进入这种状态的情况有三种:
  • 要么是新的线程被调用 start()方法

  • 要么就是运行状态执行了yield()方法放弃了执行权回到就绪状态重新排队

  • 要么就是线程阻塞解除,进入就绪状态。

  1. 运行状态:线程终于可以执行自己run()方法里面的代码,直到运行结束或者其它因素才会终止运行。
  2. 阻塞状态:线程因为某些因素终止执行,进入阻塞状态。(只有从运行状态才能进入阻塞状态)
  3. 死亡状态:线程执行完毕或者被调用了destroy()等方法(赐死)。

🦁 常用的方法

1. 线程休眠

sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。sleep方法的参数为休眠的毫秒数。

Thread.sleep(5000);			//休眠5s

2. 线程让步

yield()方法:让当前正在运行的线程回到就绪状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。

tips:

  • yield是一个静态的方法。
  • 调用yield后,yield告诉当前线程把运行机会交给具有相同优先级的线程。
  • yield不能保证,当前线程迅速从运行状态切换到就绪状态。
  • yield只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。
Thread.yield();

3. 线程联合

使得线程由并行变串行

join()方法:当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。
eg:
场景:实现爸爸让儿子买烟。

/**
 * 儿子买烟线程
 */
class SonThread implements Runnable{


  @Override
  public void run() {
    System.out.println("儿子出门买烟");
    System.out.println("儿子买烟需要10分钟");
    for(int i=0;i<10;i++){
      System.out.println("第"+i+"分钟");
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
    System.out.println("儿子买烟回来了");
   }
}


/**
 * 爸爸抽烟线程
 */
class FatherThread implements Runnable{


  @Override
  public void run() {
    System.out.println("爸爸想抽烟,发现烟抽完了");
    System.out.println("爸爸让儿子去买一包红塔山");
    Thread t = new Thread(new SonThread());
    t.start();
    System.out.println("等待儿子买烟回来");
    try {
      t.join();
     } catch (InterruptedException e) {
      e.printStackTrace();
      System.out.println("爸爸出门找儿子");
      System.exit(1);
     }
    System.out.println("爸爸高兴的接过烟,并把零钱给了儿子");
   }
}


public class TestJoinDemo {
  public static void main(String[] args) {
    System.out.println("爸爸和儿子买烟的故事");
    Thread t = new Thread(new FatherThread());
    t.start();
   }
}

4. 获取线程名称

  1. 如果是使用继承Thread类的方式,则可以直接调用this.getName()获取线程名称。
class GetName1 extends Thread{
  @Override
  public void run() {
    System.out.println(this.getName());
   }
}
  1. 如果是实现Runnable接口实现多线程方式,则可以调用Thread.currentThread().getName()获取线程名称。
class GetName2 implements Runnable{
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName());
   }
}

5. 修改线程名称

  1. 通过构造方法设置线程名称
class SetName1 extends Thread{
  public SetName1(String name){
    super(name);
   }
  @Override
  public void run() {
    System.out.println(this.getName());
   }
}


public class SetNameThread {
  public static void main(String[] args) {
    SetName1 setName1 = new SetName1("SetName1");
    setName1.start();
   }
}

  1. 通过setName()方法设置线程名称。
class SetName2 implements Runnable{


  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName());
   }
}
public class SetNameThread {
  public static void main(String[] args) {
    Thread thread = new Thread(new SetName2());
    thread.setName("SetName2");
    thread.start();
   }
}

6. 判断线程是否存活

isAlive()方法: 判断当前的线程是否处于活动状态

活动状态是指线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是存活的

class Alive implements Runnable{


  @Override
  public void run() {
    for(int i=0;i<4;i++){
      System.out.println(Thread.currentThread().getName()+" "+i);
      try {
        Thread.sleep(500);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


public class TestAliveThread {
  public static void main(String[] args) {
    Thread thread = new Thread(new Alive());
    thread.setName("Alive");
    thread.start();
    System.out.println(thread.getName()+" "+thread.isAlive());
    try {
      Thread.sleep(4000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    System.out.println(thread.getName()+" "+thread.isAlive());
   }
}

🦁 最后

这期的分享到这里,下期见!

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

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

相关文章

AI故事:智慧学校的人脸识别奇幻之旅

人脸识别 在一个名为智慧学校的小镇上&#xff0c;生物老师Rita和她的丈夫朝哥&#xff0c;一个富有创造力的艺术家&#xff0c;过着幸福美满的生活。他们的家庭与学校紧密相连&#xff0c;成为了一座小小的教育乐园。 智慧学校里有一群充满朝气的学生&#xff0c;其中小枣是…

自定义属性,v-bind computed的使用

0.0 自定义组件的使用 【掌握】 先自定义自己的组件 引入组件 import 组件名 from 路径/文件名 注册组件 <script> export default {components:{ // 组件注册组件名:组件名&#xff0c;组件名1},data(){ // 数据return {}},methods:{ // 方法} ​ } ​ </script&…

buu [AFCTF2018]MyOwnCBC 1

题目描述&#xff1a; 三份文件 #!/usr/bin/python2.7 # -*- coding: utf-8 -*-from Crypto.Cipher import AES from Crypto.Random import random from Crypto.Util.number import long_to_bytesdef MyOwnCBC(key, plain):if len(key)!32:return "error!"cipher_t…

lwIP更新记03:IPv6

从 lwIP-2.0.0 开始&#xff0c;lwIP 终于有可用的 IPv6 协议栈了&#xff01;IPv6 支持 双栈&#xff08;IPv4 和 IPv6 同时使用&#xff09; 或 IPv4/IPv6 二选一 模式。 lwIP-1.4.1 版本也有 IPv6&#xff0c;但那是实验性质的&#xff08;见…\lwip-1.4.1\src\core\ipv6目…

linux专题:嵌入式linux系统启动流程基础分析

目录 第一&#xff1a;linux内核源码基本简介 第二&#xff1a;uboot启动分析 第三&#xff1a;内核源码分析 第一&#xff1a;linux内核源码基本简介 下载 Linux 内核网址&#xff1a; https://www.kernel.org/ 最新 Linux 内核是 5.15 版本。现在常用 Linux 内核源码为4…

八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序

目录 前言 直接插入排序&#xff08;Insertion Sort&#xff09; 一、概念及其介绍 二、过程图示 三、代码 四、复杂度 希尔排序&#xff08;Shell Sort&#xff09; 一、概念 二、实现思路 三、图示过程 四、代码 4.1代码 4.2运行结果 4.3解释 五、复杂度 堆排…

路径规划算法:基于蝙蝠算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于蝙蝠的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于蝙蝠的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法蝙蝠算法来进…

Swift 如何闪电般异步读取大文件?

功能需求 Apple 系统中&#xff08;iOS、MacOS、WatchOS等等&#xff09;读取文件是一个平常的不能再平常的需求&#xff0c;不过当文件很大时&#xff0c;同步读取文件会导致 UI 的挂起&#xff0c;这是不能让用户接受的。 所以&#xff0c;要想读取文件内容的同时保持界面操…

KMP算法及其改进图文详解

文章目录 KMP算法详解什么是KMP算法KMP算法的应用场景KMP算法和暴力求解的比较字符串的前缀、后缀和最长相等前后缀KMP算法实现字符串匹配的具体过程&#xff08;图解&#xff09;从串与主串的下标变化j回退的位置(从串的下标变化)主串的下标变化 Next数组如何运用代码逻辑计算…

[CTF/网络安全] 攻防世界 xff_referer 解题详析

[CTF/网络安全] 攻防世界 xff_referer 解题详析 XFF及refererXFF格式referer格式姿势总结 题目描述&#xff1a;X老师告诉小宁其实xff和referer是可以伪造的。 XFF及referer X-Forwarded-For&#xff08;简称 XFF&#xff09;是一个 HTTP 请求头部字段&#xff0c;它用于表示 …

深入理解计算机系统第七章知识点总结

文章目录 详解ELF文件-> main.o前十六个字节的含义推测elf的大小查看节头部表推断每个section在elf中的具体位置查看.text的内容查看.data的内容关于.bss查看.rodata的内容关于其他的节表示的信息 详解符号表符号编译器如何解析多重定义的全局符号静态库与静态链接构造和使用…

seata的部署和集成

seata的部署和集成 一、部署Seata的tc-server 1.下载 首先我们要下载seata-server包&#xff0c;地址在http://seata.io/zh-cn/blog/download.html 2.解压 在非中文目录解压缩这个zip包&#xff0c;其目录结构如下&#xff1a; 3.修改配置 修改conf目录下的registry.conf文…

开源大模型资料总结

基本只关注开源大模型资料&#xff0c;非开源就不关注了&#xff0c;意义也不大。 基座大模型&#xff1a; LLaMA&#xff1a;7/13/33/65B&#xff0c;1.4T token LLaMA及其子孙模型概述 - 知乎 GLM&#xff1a;6/130B&#xff0c; ChatGLM基座&#xff1a;GLM&#xff08…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - 网际协议IP

目录 一、概述 二、初步了解网际协议 IP  &#x1f449;2.1 与数据链路层的区别  &#x1f449;2.2 网际协议 IP 概览  &#x1f449;2.3 分层的意义 三、IP协议基础知识  &#x1f449;3.1 IP地址属于网络层地址  &#x1f449;3.2 路由控制  &#x1f449;3.3 IP分包与…

solr快速上手:核心概念及solr-admin界面介绍(二)

0. 引言 上一节&#xff0c;我们简单介绍了solr并演示了单节点solr的安装流程&#xff0c;本章&#xff0c;我们继续讲解solr的核心概念 solr快速上手&#xff1a;solr简介及安装&#xff08;一&#xff09; 1. 核心概念 核心&#xff08;索引/表&#xff09; 在es中有索引…

【软件测试】5年测试老鸟总结,自动化测试成功实施,你应该知道的...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试 Pytho…

基于html+css的图展示82

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

chatgpt赋能Python-pythonage

Pythonage - 一款优秀的Python SEO工具 无论是个人博客还是商业网站&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;都是最重要的。Pythonage是一款优秀的Python SEO工具&#xff0c;可以帮助你优化你的网站并提高搜索引擎排名。在这篇文章中&#xff0c;我们将详细介绍…

ChatGPT 使用 拓展资料:开始构建你的优质Prompt

ChatGPT 使用 拓展资料:开始构建你的优质Prompt

【JavaEE】阻塞队列、定时器和线程池

目录 1、阻塞队列 1.1、概念 1.2、生产者消费者模型 1.3、阻塞队列的模拟实现 2、定时器 2.1、使用标准库中的定时器 2.2、模拟实现定时器 3、线程池 3.1、标准库中的线程池 3.1.1、ThreadPoolExecutor类的构造方法 3.1.2、Java标准库的4种拒绝策略【经典面试题】…