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

news2025/5/25 23:39:10

在这里插入图片描述

  • 👑专栏内容: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{
    @Override
    public 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(){
            @Override
            public void run() {
                System.out.println("这是线程运行代码!");
            }
        };
        thread.start();
    }

图片描述

在这里插入图片描述

Ⅱ、Runnable匿名内部类写法

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public 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.coloradmin.cn/o/1381670.html

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

相关文章

vue开发H5 实现滑动图片获取验证码

<template><div><van-button type"primary" click"sendVerification">获取验证码</van-button><van-popupv-model"captchaVisible"closeableposition"bottom"class"login-captcha":close-on-cli…

基于JAVA+SSM框架开发的志愿者服务管理系统设计与实现【附源码】

&#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统 &#x1f4dd; &#x1f680;&#x1f680;&#x1f6…

强化学习(一)简介

强化学习这一概念在历史上来源于行为心理学&#xff0c;来描述生物为了趋利避害而改变自己行为的学习过程。人类学习的过程其实就是为达到某种目的不断地与环境进行互动试错&#xff0c;比如婴儿学习走路。强化学习算法探索了一种从交互中学习的计算方法。 1、强化学习 强化学…

31 树的存储结构一

无法直接用数组表示树的逻辑结构&#xff0c;但是可以设计结构体数组对节点间的关系进行描述&#xff1a;【如表】 这样做的问题&#xff1a; 可以利用 组织链表 parent指针&#xff1a; 注意&#xff1a;树结点在 组织链表 中的位置不代表树的任何逻辑关系 树的架构图&#xf…

DNS 在互联网中的作用是什么?

互联网的运作离不开DNS&#xff0c;那么DNS到底是什么&#xff1f;它在互联网中发挥着怎样的作用呢&#xff1f; DNS&#xff0c;全称为Domain Name System&#xff0c;即域名系统&#xff0c;是互联网的一项基础设施。它负责将人们易于记忆的域名&#xff08;如www.example.…

MySQL 从零开始:06 数据检索

文章目录 1、数据准备2、限制结果3、完全限定名4、排序检索 所谓数据检索&#xff0c;就是前面所讲的”增删改查“的”查“。 注&#xff1a;本文使用的“行”指数据表中的“记录”&#xff0c;“列”指数据表中的“字段”。 在第四节《表的增删改查》中已经介绍了 select 查询…

C# Cad2016二次开发api(三)

直线 Line 属性中文数据类型作用Length长度double直线的长度Angle角度double直线的弧度&#xff0c;0~2πDelta增量Vector3d起点到终点的向量Normal法向向量Vector3d直线所在平面的法向单位向量Thickness厚度doubleEndPoint终点Point3d直线的终点StartPoint起点Point3d直线的起…

Memory Wall in Neural Network Inference

Memory Wall in Neural Network Inference 神经网络推理的瓶颈在于访存带宽&#xff0c;通常无法发挥出加速器的全部算力。本文总结了目前常用的推理加速器及其设计&#xff0c;并分析了常用神经网络的访存瓶颈。文章大部分内容参考自Computer Architecture: A Quantitative A…

JavaScript基础(26)_dom增删改练习

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>DOM增删改练习</title><link rel"stylesheet" href"../browser_default_style/reset.css"><style>table {borde…

软件测试|Selenium StaleElementException异常的原因与方法

简介 在使用Selenium进行Web自动化测试时&#xff0c;我们可能会遇到StaleElementException异常。这个异常通常在我们操作一个之前已经找到的元素时抛出&#xff0c;但该元素已经不再处于DOM树中&#xff0c;导致元素过时&#xff08;stale&#xff09;。本文将介绍StaleEleme…

C++11 左右值引用、移动语义

右值引用和移动语义 什么是左值&#xff1f;什么是左值引用&#xff1f; 左值是一个表示数据的表达式(如变量名或解引用的指针)&#xff0c;我们可以获取它的地址可以对它赋值&#xff0c;左值可以出现赋值符号的左边&#xff0c;右值不能出现在赋值符号左边。定义时const修饰…

SpringSecurity入门demo(二)表单认证

上一篇博客集成 Spring Security&#xff0c;使用其默认生效的 HTTP 基本认证保护 URL 资源&#xff0c;下面使用表单认证来保护 URL 资源。 一、默认表单认证&#xff1a; 代码改动&#xff1a;自定义WebSecurityConfig配置类 package com.security.demo.config; import or…

基于python的基金分析可视化系统

项目背景&#xff1a; 随着金融市场的发展和人们对财富管理的重视&#xff0c;越来越多的人开始参与基金投资。然而&#xff0c;由于市场上基金产品众多且复杂&#xff0c;投资者往往难以快速准确地获取基金信息和进行分析&#xff0c;从而导致投资决策不够科学和有效。为了解决…

PyCharm安装使用教程2024

简介 PyCharm是一种PythonIDE&#xff08;Integrated Development Environment&#xff0c;集成开发环境&#xff09;&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单…

三棋先手必胜证明

目录 创作原因 游戏规则 初始状态图 证明过程 先手必胜的证明 失败的博弈树&#xff08;三个多小时的成果&#xff09; 创作原因 这个棋不是网上流行的成三棋&#xff0c;我也不知道这个棋叫什么。由于这个棋是&#xff08;横竖斜&#xff09;连成三个就获胜&#xff0c;…

Vulnhub-HACKSUDO: PROXIMACENTAURI渗透

文章目录 一、前言1、靶机ip配置2、渗透目标3、渗透概括 开始实战一、信息获取二、端口敲门三、web密码爆破四、getShell五、获取新用户六、提权 一、前言 由于在做靶机的时候&#xff0c;涉及到的渗透思路是非常的广泛&#xff0c;所以在写文章的时候都是挑重点来写&#xff0…

【同济子豪兄斯坦福CS224W中文精讲】NetworkX代码学习笔记

文章目录 安装配置创建图可视化图图数据挖掘参考资料 安装配置 matplotlib中文字体设置 import networkx as nx import matplotlib.pyplot as plt # 魔法指令&#xff0c;设置后在jupyter notebook中绘制的图形会显示在输出单元格中&#xff0c;而不是弹出一个新窗口 %matplo…

ssm基于Javaweb的网上奶茶店系统的设计与实现论文

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通话…

爬虫之牛刀小试(四):爬取B站番剧的简介

今天爬取的是b站。 如何爬取b站中的番剧呢&#xff1f; 首先我们来到番剧索引中&#xff0c;随便点开一部动漫&#xff0c;检查代码。 每个作品对应一个链接: https://www.bilibili.com/bangumi/play/ss…&#xff08;ss后面的数字称为ss号&#xff09; 发现关于动漫的信息…

【数据库】sql优化有哪些?从query层面和数据库层面分析

目录 归纳sql本身的优化数据库层面的优化 归纳 这类型问题可以称为&#xff1a;Query Optimization&#xff0c;从清华AI4DB的paper list中&#xff0c;该类问题大致可以分为&#xff1a; Query RewriterCardinality EstimationCost EstimationPlan Optimization 从中文的角…