Java多线程:初识多线程!左手画方,右手画圆

在这里插入图片描述

  • 👑专栏内容:Java
  • ⛪个人主页:子夜的星的主页
  • 💕座右铭:前路未远,步履不停

目录

  • 一、线程与进程
  • 二、创建线程方法
    • 1、继承`Thread`类
    • 2、实现`Runnable`接口
    • 3、两者区别
    • 4、举个栗子
    • 5、简洁写法
      • Ⅰ、`Thread`匿名内部类写法
      • Ⅱ、`Runnable`匿名内部类写法
      • Ⅲ、使用`Lambda`表达式


嗨!想象一下,你的面前有一张展开的白纸,而你的左手和右手各握一支画笔。你的任务是在同一时间,左手画出一个方形,右手画出一个圆形。你觉得这能够完成吗?我相信大多数人会认为这是不可能完成的任务。因为我们的大脑是单线程的,一次只能专注于一项任务,一心二用在绝大多数情况下是不可行的。然而,计算机不同,它具备多线程编程的能力,可以同时处理多个任务。现在,让我们一起探索Java中多线程这个神奇的功能吧。
在这里插入图片描述

一、线程与进程

进程是计算机中执行中的程序的实例。它是操作系统进行管理和调度的基本单位

线程,有时被称为轻量级进程,是程序中的一个单独的执行流。它是操作系统调度的最小单位。与进程相比,线程之间的上下文切换通常更为迅速和高效。一个线程就是一个 “执行流”。每个线程之间都可以按照顺讯执行自己的代码、多个线程之间 “同时” 执行着多份代码。

  • 进程是操作系统能独立调度的最小单位,而线程是进程中可并发执行的单元;
  • 一个应用程序至少包括1个进程,而1个进程包括1个或多个线程;
  • 每个进程在执行过程中拥有独立的内存单元,而一个进程的多个线程在执行过程中共享内存。

图片描述

那我们来简单的类比一下吧!

image-20230818164204908
计算机的CPU就像一个工厂,而进程就是工厂里面的车间线程就是车间里面的工人

我们能够发现,多核CPU对应多个工厂,这些工厂可以从事不同的生产任务。比如有炼铁的、炼油的、食品加工等等。在一个工厂中,又可以包含一个或多个车间(进程),但是由于电力资源有限,同一时间就只能有一个车间在运行。换句话说,对于单核CPU,同一时间只能处理一个进程,其他进程处在等待状态。而一个车间中,可以有一个或多个工人(线程)协同工作,他们共享内部资源,共同完成任务。

同一个进程中,这些线程共用同一份资源(内存 + 硬盘…),但是每个线程独立去CPU上调度(状态、上下文、优先级、记账信息…)各自有各自的一份

所以,进程是操作系统进行分配的基本单位。而线程是操作系统进行调度执行的基本单位。
在这里插入图片描述
【面试题】谈谈进程和线程的区别和联系

  • 进程是包含线程的,都是实现并发编程的方式,但是线程比进程更轻量。

  • 进程是系统分配资源的基本单位,线程是系统调度的基本单位。创建进程的时候,把分配资源(虚拟地址空间、文件描述符表)的工作给做了,后面创建线程,直接共用资源即可。

  • 进程有独立的地址空间,彼此之间不会相互影响到,进程的独立性。多个线程共用这份地址空间,一个线程一旦抛出异常,就可能会导致进程异常结束。

  • 进程和进程之间不共享内存空间,同一个进程的线程之间共享同一个内存空间。

image-20230818211422931
在这里插入图片描述

二、创建线程方法

在 Java 中,有两种创建多线程的方式,分别是继承 Thread 类和实现 Runnable 接口。下面让我们一起来学习下这两种创建多线程的方法吧。

1、继承Thread

class Mythread extends Thread{public void run() {System.out.println("这是线程运行代码!");}
}public class Main {public static void main(String[] args) {Mythread mythread = new Mythread();mythread.start();}
}

创建一个类,继承 Thread ,重写run方法,该方法包含线程的实际执行逻辑。

start()方法:start() 方法是Thread类的一个方法,它用于启动线程并调用线程的run()方法。
在这个例子中,myThread.start() 会启动一个新的线程,并在新线程中执行 run() 方法中定义的代码。

如果把myThread.start();改成myThread.run();后,运行的结果也是hello world。这两个有什么不一样呢?

run只是上面的入口方法,并没有调用系统api也没有创建真正的线程来。

图片描述

2、实现Runnable接口

class Myrunnable implements Runnable{public void run() {System.out.println("这是线程运行代码!");}
}public class Main {public static void main(String[] args) {Thread thread = new Thread(new Myrunnable());thread.start();}
}

该方法主要是创建一个类,实现 Runnable 接口,重写run方法。

3、两者区别

Thread这里是直接把要完成的工作,放到了ThreadRun方法中

Runnable这里,则是分开了,把要完成的工作放到了Runnable中,再让RunnableThread配合。

把线程要执行的任务和线程本身进一步解耦合了。并发编程中,来完成某个工作,使用Runnable描述这个工作的具体细节。这样使用多线程的方式,就可以使用Runnable来搭配线程使用。使用线程池的方式,就可以使用Runnable来搭配线程池使用,使用协议的方式,也可以使用Runnable搭配协程。

特征继承 Thread实现 Runnable 接口
类的继承关系直接继承 Thread 类。类实现 Runnable 接口,可以继承其他类。
创建方式创建类的子类,重写 run() 方法。创建类,实现 Runnable 接口,实现 run() 方法。
多线程对象创建直接创建线程对象的实例。先创建 Runnable 对象,再通过 Runnable 对象创建 Thread 对象。
启动线程直接调用 start() 方法。调用 Thread 类的构造方法,传递 Runnable 对象,然后调用 start() 方法。
资源共享较难实现资源共享。更容易实现资源共享,因为可以共享同一个 Runnable 对象。
多态使用难以使用多态,因为已经继承了 Thread 类。更容易使用多态,因为类可以继续实现其他接口或继承其他类。

4、举个栗子

回到上面的问题。把myThread.start();改成myThread.run();后,运行的结果也是hello world。
这两个有什么不一样呢?我们来举个栗子看看吧。

图片描述

class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("hello thread");}}
}
public class Demo1 {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();//myThread.run();while(true){System.out.println("hello main");}}
}

image-20230818223628990
使用myThread.start();运行的程序,会同时打印hello thread 和 hello main

两个循环都在执行,两个线程分别执行自己的循环,并发性的执行。

而使用myThread.run();运行的程序,只会打印hello main

只执行了一个循环。

在这里插入图片描述

5、简洁写法

我们可以通过匿名内部类来ThreadRunnable这两种写法。
什么是匿名内部类?

匿名内部类是一种在声明和创建类的对象同时进行的特殊形式,它没有显式的类名称。这种方式通常用于在使用类的地方定义类的实例,尤其是在创建接口的实现或继承抽象类的实例时。

Ⅰ、Thread匿名内部类写法

    public static void main(String[] args) {Thread thread = new Thread(){@Overridepublic void run() {System.out.println("这是线程运行代码!");}};thread.start();}

图片描述

在这里插入图片描述

Ⅱ、Runnable匿名内部类写法

    public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("这是线程运行代码!");}});thread.start();}

在这里插入图片描述

图片描述

Ⅲ、使用Lambda表达式

Lambda表达式主要用于实现函数式接口,Lambda表达式本质上就是一个匿名函数,像这样的匿名函数可以做回调函数来使用。

图片描述

匿名函数,就是没有命名的函数。它具有传递函数体的能力,却无需提前声明整个函数的结构。

回调函数则是在程序执行中,不需要程序员主动调用的函数。相反,它会在特定的时机或条件下,由系统或其他代码自动触发执行。程序员通过将函数传递给其他代码,实现了一种回调机制,使得在适当的时候,这个函数会被调用,完成特定的操作。

好了,废话不多说,先用Lambda表达式写一下吧。

    public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("这是线程运行代码!");});thread.start();}

Thread thread = new Thread(() -> {
这里使用Lambda表达式创建了一个新的Thread对象,构造方法接受一个Runnable接口的实现作为参数。Lambda表达式的内容在大括号中,用于定义线程运行时执行的代码块。

图片描述

线程的创建并不是只有这五种写法,还可以用线程池和Callable写法。这个我们后面再聊!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue 中修改 Element 组件的 下拉菜单(Dropdown) 的样式

Vue 中修改 Element 组件的 下拉菜单(Dropdown) 的样式 今天在项目中碰到一个 UI 改造的需求,需要根据设计图把页面升级成 UI 设计师提供的设计图样式。 到最后页面改造完了,但是 UI 提供的下拉菜单样式全部是黑色半透明的,只能硬着头皮改了。…

[NOIP2006 提高组] 作业调度方案(修改)

题目: 这里对于之前的题目进行修改记录。果然还是受不了等待,利用晚饭时间又看了这个题目。于是发现了问题。 之前的博客:https://blog.csdn.net/KLSZM/article/details/135522867?spm1001.2014.3001.5501 问题修改描述 上午书写的代码中是…

软件测试常问问题

1.fiddler抓包过程是什么?抓包的目的是什么? 抓APP包,需要在APP上设置代理,设置网络也就是WiFi设置的地方,设置的代理地址也就是fiddler上面的IP地址,(查找本机IP地址,cmd进入黑窗口…

主流浏览器设置代理IP之Firefox浏览器

给浏览器设置代理IP是目前代理IP的主流使用场景之一,接下来小编就手把手教你如何对Firefox浏览器进行代理IP设置 注:本次使用IP来源于携趣代理平台https://www.xiequ.cn/index.html?4c51b56bhttps://www.xiequ.cn/index.html?4c51b56bFirefox浏览器内…

Frida基本能力汇总

1 需求 2 接口 3.1 基本能力Ⅰ:hook参数、修改结果 3.2 基本能力Ⅱ:参数构造、方法重载、隐藏函数的处理 3.3 中级能力:远程调用 3.4 高级能力:互联互通、动态修改 4 参考资料 https://github.com/hookmaster/frida-all-in-one/t…

ChatGPT到底能做什么呢?

1、熟练掌握ChatGPT提示词技巧及各种应用方法,并成为工作中的助手。 2、通过案例掌握ChatGPT撰写、修改论文及工作报告,提供写作能力及优化工作 3、熟练掌握ChatGPT融合相关插件的应用,完成数据分析、编程以及深度学习等相关科研项目。 4、…

ES6前端学习笔记

修正 ES6是ECMA为JavaScript制定的第6个标准版本,相关历史可查看此章节《ES6-ECMAScript6简介》。 标准委员会最终决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案…

DrGraph原理示教 - OpenCV 4 功能 - 边界填充

今天简单来看一下OpenCV中的边界填充 param src Source image. param dst Destination image of the same type as src and the size Size(src.colsleftright, src.rowstopbottom) . param top the top pixels param bottom the bottom pixels param left the left pixels par…

源码搭建教学:连锁餐饮APP开发实战

连锁餐饮APP,对于很多从事餐饮行业的人来说不会陌生,同样这个项目本身就有着很高的热度。今天,小编将深入为大家讲述一下此系统的前后端开发、数据库设计、用户界面设计等方面,让您深入了解全栈开发的方方面面。 一、项目准备与规…

创建一个简单鸿蒙app项目

文章目录 前言TypeScript 基础类型创建一个鸿蒙app总结 一、前言 鸿蒙系统上的开发已经是趋势了,必须紧跟时代的潮流。先简单了解下鸿蒙系统中,我们开发一个app需要用到的语言,那么就是TypeScript。这篇文章主要讲的就是一些基础的语法。最…

【C++】基础:STL字符串库string

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍STL字符串库string。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路&am…

【Linux】常见指令解析下

目录 前言1. cp指令(重要)2. mv指令 (重要)3. cat指令4. more指令5. less指令 (重要)6. head指令7. tail指令8. 时间相关的指令8.1 data显示8.2 时间戳 9. cal指令10. find指令(非常重要&#x…