当前位置: 首页 > news >正文

多线程(1)——认识线程

目录

  • 概念
    • 线程是什么
    • 为什么要有线程
    • 进程和线程的区别
    • Java的线程 和 操作系统线程 的关系
  • 创建线程
    • 方法1:继承Thread 类
      • run和start方法
    • 方法2:实现Runnable 接口
      • 方法1和方法2的区别
    • 方法3:通过匿名内部类继承Thread
    • 方法4:通过匿名内部类实现Runnable,重写run,搭配Thread
    • 方法5:基于Lambda表达式创建Thread


在介绍多线程前,先介绍一下操作系统的概念
操作系统是一个管理的软件
1.对下管理各种硬件设备
2.对上给软件提供稳定的运行环境

概念

线程是什么

一个线程就是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程之间同时执行着多份代码

线程过多,因为CPU核心书名是有限的,这些线程在CPU上的调度开销会越来越明显,也会影响效率
所以要根据CPU资源合理增加线程数量

为什么要有线程

进程和线程的区别

进程是运行起来的程序,每个进程通过链表连接起来,进程有两种状态 并发(一个CPU核心同时执行一个线程,但因为运行/切换速度极快,可以看做是同时在执行多个线程) 和 并行(两个CPU核心同时执行两个线程) ,而每个进程中 有多个 线程,这些线程共用该进程的资源(内存资源,文件描述符表),每个线程都可以独立执行一段逻辑,并且独立在 CPU 上调度
当进程已经有了,在进程内部再创建新线程,就把申请资源开销省下来了

进程包含线程(一个进程至少有一个线程,对于Java程序中,main方法所在的线程就是主线程)

进程是操作系统资源分配的基本单位
线程是操作系统调度执行的基本单位

Java的线程 和 操作系统线程 的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对⽤⼾层提供了⼀些 API(Java内部的方法) 供⽤⼾
使⽤(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类(线程)可以视为是对操作系统提供的 API 进⾏了进⼀步的抽象和封装,而进程封装的很粗糙,多进程变成的很多能力,JVM原生API没有提供,本身Java设计者就不鼓励多进程编程

创建线程

方法1:继承Thread 类

class myThread extends Thread{@Overridepublic void run(){while(true){System.out.println("Hello, Thread!");try {//这里只能用try catch ,因为父类Thread的run方法没有抛出异常,子类myThread的run方法也不能抛出异常//如果这里用throws,父类和子类方法声明就不一致,就不满足重写的要求Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread thread=new myThread();//thread.start();thread.run();//如果这里调用run方法,就不会启动线程,而是在main线程中执行run方法,不会并行执行while (true) {System.out.println("Hello, main!");Thread.sleep(1000);}}
}

注意,上述线程运行时myThread线程和main线程在操作系统中的调度顺序是无序,不可预测的,是随机的。即可能会先打印 Hello, Thread!,也可能先打印 Hello, main!

run和start方法

在上述方法中,重写的是Thread的run方法,但是在main方法中调用的是start方法,这么做的原因是:
start方法是调用系统的API(Java 本地方法(封装了对系统底层的调用)),它才是真正在操作系统内部创建一个线程
这个新的线程就会以run方法为入口(执行run中的逻辑)
run方法不需要代码中显示调用,run 方法执行完成后线程进入销毁阶段。

如果在main中直接调用thread.run(),就没有创建新的线程,而是直接在main方法所在的 主线程 中执行了run的逻辑。如果在main中调用thread.start(),就会开启一个新线程和main线程并发执行

方法2:实现Runnable 接口

实现Runnable接口的方式,是把线程的任务单独提取出来,放到一个类中,然后再创建线程时,把这个类的实例作为参数传递给Thread的构造方法。这样,线程的任务就被封装到了一个类中,使得代码更加清晰,也便于维护和扩展。

class MyRunnable implements Runnable{@Overridepublic void run(){while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Demo2 {public static void main(String[] args) throws InterruptedException {/**/Runnable runnable=new MyRunnable();//runnable没有start方法,项启动线程要搭配ThreadThread thread=new Thread(runnable);thread.start();while (true) {System.out.println("hello main");Thread.sleep(1000);}}
}

方法1和方法2的区别

对于第一种写法(继承thread类),描述任务的时候,认为代码是写到Thread子类中的
此时任务内容 和 Thread 类的耦合度更高
第二种方法,任务写到Runnable中,不涉及到线程相关的概念,任务内容和Thread的耦合度很小,几乎没有,这个任务就能放进其他来执行(进程,协程)

耦合度:在编码中,更倾向于写耦合度低的代码,方便修改,使用

方法3:通过匿名内部类继承Thread

Thread thread=new Thread(){//创建了一个没有名字的匿名内部类public void run(){}};

使用举例

public class Demo3 {public static void main(String[] args) {Thread thread=new Thread(){//创建了一个没有名字的匿名内部类//这个类是Thread的子类,子类重写了run方法//同时也创建了子类实例,通过 thread 指向public void run(){while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};thread.start();}
}

像这样的写法的目的就是为了简化代码

方法4:通过匿名内部类实现Runnable,重写run,搭配Thread

public class Demo4 {public static void main(String[] args) {Runnable runnable=new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread thread=new Thread(runnable);thread.start();}
}

方法5:基于Lambda表达式创建Thread

语法:

Thread thread=new Thread(()->{/*此处写run方法内容*/});

lambda表达式,本质上是 匿名方法
直接使用 lambda 作为入口方法

使用举例:

public class Demo5 {public static void main(String[] args) {Thread thread=new Thread(()->{while(true){System.out.println("hello thread");try {Thread.sleep(1000, 0);} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();}
}

相关文章:

  • 摩尔投票法详细介绍
  • 【C】初阶数据结构12 -- 冒泡排序
  • QT跨平台软件开发要点
  • 微信小程序鲜花销售系统设计与实现
  • ASP.NET MVC​ 入门指南三
  • QNX系统动态配置动态库.so文件日志打印级别的方法
  • 7.13 GitHub Sentinel全链路测试实战:自动化框架+零误差传输,QPS提升6倍!
  • 进行物联网安全PoC时的注意事项
  • 深度探索:DeepSeek赋能WPS图表绘制
  • 可视化性能分析工具火焰图
  • Astro canvas大屏从iotDA上抽取设备影子的参数的详细操作实施路径
  • 【Java并发】【原子类】适合初学体质的原子类入门
  • 树状数组底层逻辑探讨 / 模版代码-P3374-P3368
  • 精益数据分析(29/126):深入剖析电子商务商业模式
  • Spring和Spring Boot集成MyBatis的完整对比示例,包含从项目创建到测试的全流程代码
  • Windows 安装 MongoDB 教程
  • 【OpenCV】第二章——图像处理基础
  • 计算机学报 2024年 区块链论文 录用汇总 附pdf下载
  • WPS中论文如何加参考文献
  • 树状数组详解
  • 向总书记汇报具身智能发展的“稚辉君”:从期待到兴奋再到备受鼓舞
  • 在岸、离岸人民币对美元汇率双双升破7.26关口
  • 俄宣布停火三天,外交部:希望各方继续通过对话谈判解决危机
  • 中方发布《不跪!》视频传递何种信息?外交部回应
  • 王毅会见泰国外长玛里:坚决有力打击电诈等跨境犯罪
  • 夜读丨怀念那个写信的年代