Java多线程编程基础
目录
编写第一个多线程程序
1. 方式一 : 继承Thread类, 重写run方法
2. 方式二: 实现Runnable接口, 重写run方法
3. 方式三: 使用Lambda表达式
[匿名内部类]
[Lambda表达式]
在上个文章中, 我们了解了进程和线程的相关概念. 那么, 在Java中, 我们如何进行多线程编程呢?
线程本身是操作系统的一个概念, 操作系统提供一个线程的API, 供程序员调用. 不同的操作系统线程的API是不同的 (Windows的线程API和Linux的线程API差异很大). 但是JVM将不同的系统线程的API都封装好了, 所以我门在不同系统上进行Java的多线程编程时, 不需要关注系统原生的API, 只需要掌握Java这一套API就可以了.
在Java中, Thread类就负责完成多线程的相关开发.
编写第一个多线程程序
我们有多种方式来进行多线程编程.
1. 方式一 : 继承Thread类, 重写run方法
(1) 步骤一: 创建一个线程类

[注]: 这里不需要对Thread进行导包操作, 因为Thread是java.lang包中的类, 默认已经导入.
上述代码, 就创建了一个线程类(MyThread类) 继承自Thread类. 类里重写了run()方法. (run()方法是Runnable接口中定义的一个方法, 用来指定线程要执行的任务, Thread类实现了run方法, 我们定义的MyThread类继承自Thread, 自然需要重写run方法, 来制定我们当前这个线程需要完成什么任务)
(2) 步骤二: 启动线程

观察运行结果, 我们可以看到, 现在控制台上正在循环打印"hello thread", 代表我们创建的MyThread线程启动了. 这里涉及到一个方法start(), 这个方法的作用就是启动线程.
[注]: 通过上述代码, 我们看到, main方法中并没有调用run()方法, 但是程序确实执行力main方法里面的内容. 像这种, 我们手动定义但没有手动调用, 被系统自动调用执行的方法, 叫做"回调函数"(Callback Function)
接下来我们整体看这个代码:
class MyThread extends Thread {@Overridepublic void run() {// 这里写的代码, 就是即将创建出的线程, 要执行的逻辑.while (true) {System.out.println("hello thread");// 该线程需要完成的任务: 循环打印"hello thread".}}
}public class Demo1 {public static void main(String[] args) {MyThread t = new MyThread();t.start(); // 这一步就创建了一个线程}
}
上述代码实际上就是一个进程. 因为调用了main方法, 所以该进程中还有一个执行main方法的线程, 就叫做"主线程". 我们之前说过, 一个进程至少包含一个线程, 这个线程就是主线程.
主线程和t线程会并发/并行地在CPU上调度执行. 宏观表现就是交替打印主线程和t线程中的任务.
class MyThread extends Thread { //创建一个线程类@Overridepublic void run() {// 这里写的代码, 就是即将创建出的线程, 要执行的逻辑.while (true) {System.out.println("hello thread");// 该线程需要完成的任务: 循环打印"hello thread".}}
}public class Demo1 {public static void main(String[] args) {MyThread t = new MyThread(); //实例化一个新线程t.start(); //启动这个线程while (true) {System.out.println("hello main");// 主线程要完成的任务: 循环打印"hello main".}}
}

从运行结果来看, 主线程和t线程的任务交替打印. 那么多个线程之间, 谁先去CPU上调度执行, 谁后去CPU上调度执行, 这个顺序是不确定的, 取决于操作系统的内核. 并且, 我们把这种执行方式叫做"抢占式执行".
* 使用匿名内部类来实现方式一

上述红框框住的代码, 就使用了匿名内部类. 这部分代码完成了3件事: (1) 定义了一个匿名内部类, 这个类一定是Thread的子类. (2) 在这个类内部重写了run方法. (3) 创建了一个类的实例, 并且赋给了引用t.
2. 方式二: 实现Runnable接口, 重写run方法
方式一中, 我们的MyThread类是继承自Thread类的, 而Thread类又实现了Runnable接口. 那么我们能不能直接实现Runnable接口呢? 那当然是可以的.
(1) 步骤一: 创建一个MyRunnable类, 实现Runnable接口

(2) 步骤二: 实例化一个MyRunnable类的对象, 并用这个对象实例化一个新线程.
整体代码如下:
class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {System.out.println("hello thread");// 该线程需要完成的任务: 循环打印"hello thread".}}
}
public class Demo2 {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable(); //实例化一个MyRunnable类的对象Thread t = new Thread(myRunnable); // 用myRunnable实例化一个新线程t.start();while (true) {System.out.println("hello main");// 主线程需要完成的任务: 循环打印"hello main".}}
}
* 使用匿名内部类来实现方式二:
public class Demo4 {public static void main(String[] args) {
// Runnable runnable = new Runnable(){
// @Override
// public void run() {
// while (true) {
// System.out.println("hello thread");
// }
// }
// };
// Thread t = new Thread(runnable);
// // 上述代码还是比较繁琐, 我们可以再嵌套一层匿名内部类Thread t = new Thread(new Runnable(){@Overridepublic void run() {while (true) {System.out.println("hello thread");}}});// 嵌套两层匿名内部类 实例化出一个线程对象t.start(); //启动线程}
}
3. 方式三: 使用Lambda表达式
public class Demo5 {public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {System.out.println("hello thread");}}); // Lambda表达式: ()代表函数的形参列表为空; {}前面是空的,代表函数没有返回值, {}里面是方法体.}
}
( )内为空代表函数的形参列表为空; { }前面是空的,代表函数没有返回值, { }里面是方法体.
[匿名内部类]
匿名内部类(Anonymous Inner Class)是一种没有名字的内部类,通常用于创建那些只需要使用一次的类实例。匿名内部类可以继承一个类或者实现一个接口,并且可以在创建时立即进行实例化和使用。
[注]: 匿名内部类一般是"一次性使用"的类, 用完一次之后就不再使用了.
匿名内部类可以访问外部类的成员变量和方法,包括私有成员。
匿名内部类只能用于创建一个类的实例,不能用于创建多个实例。
[Lambda表达式]
Lambda表达式其实就是一个"函数式接口"产生的"匿名内部类", 实质上还是一种匿名内部类. Lambda 表达式通常用于实现函数式接口(Functional Interface),这是一个只有一个抽象方法的接口。例如,Runnable 接口只有一个 run 方法,因此可以用 Lambda 表达式来实现。
本篇文章主要讨论了如何写好你的第一个多线程程序, 快去试试吧~~
相关文章:
Java多线程编程基础
目录 编写第一个多线程程序 1. 方式一 : 继承Thread类, 重写run方法 2. 方式二: 实现Runnable接口, 重写run方法 3. 方式三: 使用Lambda表达式 [匿名内部类] [Lambda表达式] 在上个文章中, 我们了解了进程和线程的相关概念. 那么, 在Java中, 我们如何进行多线程编程呢? …...
刷代随有感(134):单调栈——下一个更大元素I(难点涉及哈希表与单调栈的结合)
单调栈处理的是下标! 题干: 代码: class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {stack<int>ddst;unordered_map<int,int>umap;vector<int…...
Linux云计算 |【第五阶段】CLOUD-DAY5
主要内容: 容器的镜像编排,commit简单镜像创建,Dockerfile制作服务镜像(语法、创建镜像)、创建复杂镜像(Docker微服务架构、示例:NGINXPHP)、私有仓库 一、简单镜像创建 1、自定义…...
被上传文件于后端的命名策略
上一篇博客我们了解了前端上传的文件资源应该存放在后端项目中的什么位置,那么随之而来的另一个问题——我们应该如何为上传的文件命名呢?往往直接采用原文件名并不稳妥,会导致命名冲突、文件冲突、数据库管理冲突等多种问题,下面…...
哈希表 算法专题
哈希表简介 是什么 存储数据的容器有啥用? "快速"查找某个元素什么时候用哈希表 频繁地查找某个数(有序用二分)怎么用哈希表 容器用数组模拟 字符串中的字符 范围比较小的数 一. 两数之和 两数之和 class Solution {public int[] twoSum(int[] nums, int targe…...
unity3d————[HideInInspector]
在Unity3D中,[HideInInspector]是一个属性修饰符,它的主要作用是在Unity的Inspector窗口中隐藏变量或属性。以下是关于[HideInInspector]的详细解释和作用: 作用 隐藏变量或属性:当你在脚本中使用[HideInInspector]修饰符时&…...
Soanrquber集成Gitlab 之 导入Gitlab项目
集成Gitlab 之 导入Gitlab项目 说明: Sonarquber里面的项目,顺便设置,只要在集成CI的时候,使用这个项目的项目标识即可。 当然项目名称一一对应是最好的了,所以这里讲导入Gitlab的项目,项目名称一一对应&…...
论区块链技术及应用
引言 区块链技术作为一种革命性的创新,近年来在全球范围内得到了广泛关注和应用。其去中心化、透明性和不可篡改的特性,使其在多个领域展现出了巨大的潜力。从金融到物联网,从医疗管理到政务监管,区块链正在改变我们处理信息和进…...
GPT避坑指南:如何辨别逆向、AZ、OpenAI官转
市面上有些说自己是官转,一刀只需要1块甚至几毛钱,并声称官方倍率的,很大可能就是使用的是 逆向或Azure。 如何鉴别逆向 逆向的种类很多,主要分为3类 逆向不知名A| 镜像站或偷的 key。成本约等于0,调用聊天数据可能在…...
Qt 文本文件读写与保存
Qt 文本文件读写与保存 开发工具:VS2013 QT5.8 设计UI界面,如下图所示 sample7_1QFile.h 头文件: #pragma once#include <QtWidgets/QMainWindow> #include "ui_sample7_1QFile.h"class sample7_1QFile : public QMainWin…...
Linux基础环境搭建(CentOS7)- 安装Scala和Spark
#Linux基础环境搭建(CentOS7)- 安装Scala和Spark Linux基础环境搭建(CentOS7)- 安装Scala和Spark 大家注意以下的环境搭建版本号,如果版本不匹配有可能出现问题!(spark不要下2.4版本的 会报错…...
SpringBoot 下的Excel文件损坏与内容乱码问题
序言 随着打包部署的方式的改变,原本正常运行的代码可能带来一些新的问题,比如我们现在使用SpringBoot 的方式生成Jar包直接运行,就会对我们再在Resource下的Excel文件产生影响,导入与预期不符的情况发生cuiyaonan2000163.com 比…...
官宣下代GPU存在缺陷,50系显卡或将迎来涨价
如果说 AMD 在 Ryzen 3000 系列还是和 intel 在 CPU 方面棋差一着的话,Ryzen 5000 系列就是打了个漂亮的翻身仗了。 凭借先进的 7nm 工艺制程和全新架构,让后来 intel 急忙推出「14nm」的 11 代酷睿也难以望其项背。 直到 intel 12 代发布的时候…...
使用pytorch实现LSTM预测交通流
原始数据: 免费可下载原始参考数据 预测结果图: 根据测试数据test_data的真实值real_flow,与模型根据测试数据得到的输出结果pre_flow 完整源码: #!/usr/bin/env python # _*_ coding: utf-8 _*_import pandas as pd import nu…...
C/C++(八)C++11
目录 一、C11的简介 二、万能引用与完美转发 1、万能引用:模板中的 && 引用 2、完美转发:保持万能引用左右值属性的解决方案 三、可变参数模板 1、可变参数模板的基本使用 2、push 系列和 emplace 系列的区别 四、lambda表达式…...
使用three.js 实现 自定义绘制平面的效果
使用three.js 实现 自定义绘制平面的效果 预览 import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.jsconst box document.getElementById(box)const scene new THREE.Scene()const camera new THREE.PerspectiveCam…...
玩转Docker | 使用Docker部署捕鱼网页小游戏
玩转Docker | 使用Docker部署捕鱼网页小游戏 一、项目介绍项目简介项目预览二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署捕鱼网页小游戏下载镜像创建容器检查容器状态下载项目内容查看服务监听端口安全设置四、访问捕鱼网页小游戏五、总结一、项目介绍…...
第2章 Android App开发基础
第 2 章 Android App开发基础 bilibili学习地址 github代码地址 本章介绍基于Android系统的App开发常识,包括以下几个方面:App开发与其他软件开发有什么不一 样,App工程是怎样的组织结构又是怎样配置的,App开发的前后端分离设计…...
通过 SYSENTER/SYSEXIT指令来学习系统调用
SYSENTER指令—快速系统调用 指令格式没有什么重要的内容,只有opcode ,没有后面的其他字段 指令的作用: 执行快速调用到特权级别0的系统过程或例程。SYSENTER是SYSEXIT的配套指令。该指令经过优化,能够为从运行在特权级别3的用户代码到特权级别0的操作系统或执行过程…...
Nginx开发实战——网络通信(一)
文章目录 Nginx开发框架信号处理函数的进一步完善(避免僵尸子进程)(续)ngx_signal.cxxngx_process_cycle.cxx 网络通信实战客户端和服务端1. 解析一个浏览器访问网页的过程2.客户端服务器角色规律总结 网络模型OSI 7层网络模型TCP/IP 4层模型3.TCP/IP的解释和比喻 最…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
