Java多线程介绍及使用指南
“多线程”:并发
要介绍线程,首先要区分开程序、进程和线程这三者的区别。
程序:具有一定功能的代码的集合,但是是静态的,没有启动运行
进程:启动运行的程序【资源的分配单位】
线程:进程中的每一条执行路径,就是线程。
概念:
并行:多个CPU同时执行多个任务
并发:一个CPU“同时”执行多个任务(采用时间片切换)
将1分钟---分成10000份=6毫秒
5.1使用线程的3种方式:
第一种方式:继承父类
1.创建线程类:
public class NumberThread extends Thread
2.创建线程对象:【新生状态】
NumberThread num=new NumberThread();
3.【就绪状态】--->cpu给资源--->运行状态
num.start();
如果调用run(),则不是多线程的了,会直接执行完
例子:售票窗口
有10张车票,3个窗口,同时售票,显示售票结果。
package thread; public class WindowThread extends Thread { static int ticket=10; public WindowThread() { } public WindowThread(String name) { super(name); } @Override public void run() { while (ticket >= 1) { System.out.println(getName()+"窗口,卖出第"+ticket+"票"); ticket--; } }
}
package thread; public class TestMain { public static void main(String[] args) { WindowThread w1=new WindowThread("第1"); w1.start(); WindowThread w2=new WindowThread("第2"); w2.start(); WindowThread w3=new WindowThread("第3"); w3.start(); }
}
优点:启动线程对象高效率
缺点:占用了父类位置
第二种方式:实现接口
public class Window implements Runnable { int ticket=10; @Override public void run() { while (ticket >= 1) { System.out.println(Thread.currentThread().getName()+"窗口卖出第"+ticket+"张票"); ticket--; } }
}
public class Test { public static void main(String[] args) { Window w1=new Window(); Thread t1 = new Thread(w1,"第1"); t1.start(); Thread t2 = new Thread(w1, "第2"); t2.start(); Thread t3 = new Thread(w1, "第3"); t3.start(); }
}
优点:没有占用父类位置,共享资源能力强,资源不用加static
缺点:启动线程对象 效率低
第三种方式:实现接口
对比第一种和第二种创建线程的方式发现,无论第一种继承Thread类的方式还是第二种实现Runnable接口的方式,都需要有一个run方法
但是这个run方法有不足:
1)没有返回值
2)不能抛出异常
基于上面的两个不足,在JDK1.5以后出现了第三种创建线程的方式:实现Callable接口:
实现Callable接口好处:(1)有返回值 (2)能抛出异常
缺点:线程创建比较麻烦
package com.msb.test05;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*** @author : msb-zhaoss*/
public class TestRandomNum implements Callable<Integer> {/*1.实现Callable接口,可以不带泛型,如果不带泛型,那么call方式的返回值就是Object类型2.如果带泛型,那么call的返回值就是泛型对应的类型3.从call方法看到:方法有返回值,可以跑出异常*/@Overridepublic Integer call() throws Exception {return new Random().nextInt(10);//返回10以内的随机数}
}
class Test{//这是main方法,程序的入口public static void main(String[] args) throws ExecutionException, InterruptedException {//定义一个线程对象:TestRandomNum trn = new TestRandomNum();FutureTask ft = new FutureTask(trn);Thread t = new Thread(ft);t.start();//获取线程得到的返回值:Object obj = ft.get();System.out.println(obj);}
}
FutureTask
是Runnable
接口的一个实现类,因此它可以作为参数传给Tread
用线程任务对象来接返回值(这里用的是ft)
注意:get方法的使用位置必须在start之后
创建几个线程对象,就要创建几个FutreTask,而不能new两个Thread,因为这样是一个对象的内容接了两遍
5.2线程对象的常用方法:
a.getName()
获得当前线程对象的名字:
---线程名 如果开发者使用了无参构造器,程序自动设置线程名Thread-0++(主线程除外)
---开发者使用有参构造器,参数的值就是线程的名字。
public class NumberThread extends Thread{ public NumberThread() { } public NumberThread(String name) { super(name); } @Override public void run() { //第二条执行路线的内容 for (int i = 1; i < 100; i++) { System.out.println(super.getName()+"i="+i); //获得当前线程名 } }
}
---借助num.setName("线程1")
,为线程名赋值
public class TestMain { //main()方法所在的线程叫主线程 public static void main(String[] args) { NumberThread num=new NumberThread("窗口1"); //此时程序开启第二条路 num.start(); NumberThread num2=new NumberThread("窗口2"); //此时程序开启第二条路 num2.start(); for (int i = 1; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"i="+i); } }
}
b.currentThread()
获得当前正在运行的线程对象
针对实现接口的情况,由于没有再继承Thread类,因此也无法直接使用其中的方法getName(),但Thread类中有静态方法currentThread(),因此可以通过这种方法获得当前运行的线程对象的信息。c.setPriority()
设置线程的优先级,1-10之间,默认是5
Thread t2 = new Thread(w1, "第2");
t2.setPriority(1); //设置线程的优先级,1-10之间,默认是5
t2.start();
d.Thread.currentThread().stop()
过期方法,不建议执行- e.强行占用。当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程。
public static void main(String[] args) throws InterruptedException { NumberThread num=new NumberThread("窗口1"); //此时程序开启第二条路 num.start(); for (int i = 1; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"i="+i); if (i == 50) { num.join(); //强势加入(num运行完了后,其余的才能执行)System.out.println("maini=50"); } }
}
f.setDaemon(true)
;伴随线程
tt.setDaemon(true)
;//设置伴随线程
主线程死亡,伴随线程也会跟着一起死亡。
public class TestThread extends Thread {@Overridepublic void run() {for (int i = 1; i <= 1000 ; i++) {System.out.println("子线程----"+i);}}
}
class Test{//这是main方法,程序的入口public static void main(String[] args) {//创建并启动子线程:TestThread tt = new TestThread();tt.setDaemon(true);//设置伴随线程 注意:先设置,再启动tt.start();//主线程中还要输出1-10的数字:for (int i = 1; i <= 10 ; i++) {System.out.println("main---"+i);}}
}
g.sleep(毫秒)
:设置线程休眠
public static void main(String[] args) throws InterruptedException { System.out.println("1111"); Thread.sleep(1000); //休眠的例子System.out.println("222");
}
5.3线程的生命周期:
使用线程构造器---创建线程对象--->线程新生状态
创建线程对象.start()--->进入到就绪状态【有资格,没资源】
线程对象.run()--->进入到运行状态【有资格,有资源】
在时间片段内,执行完--->死亡状态
在时间片段内,没执行完--->重回就绪状态
在时间片段内,出现突发事件--->阻塞状态--->就绪状态
5.4解决线程安全问题:
【第一种:同步代码块】
public void run() { while (true) { synchronized (WindowThread.class){ //WindowThread.class是监视器对象 if(ticket >= 1) { System.out.println(getName() + "窗口,卖出第" + ticket + "票"); ticket--; } } }
}
总结:
同步监视器总结:
1:认识同步监视器(锁子) ----- synchronized(同步监视器){ }
1)必须是引用数据类型,不能是基本数据类型
2)也可以创建一个专门的同步监视器,没有任何业务含义
3)一般使用共享资源做同步监视器即可
4)在同步代码块中不能改变同步监视器对象的引用
5)尽量不要String和包装类Integer做同步监视器
6)建议使用final修饰同步监视器
2:同步代码块的执行过程
1)第一个线程来到同步代码块,发现同步监视器open状态,需要close,然后执行其中的代码
2)第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去了cpu,但是没有开锁open
3)第二个线程获取了cpu,来到了同步代码块,发现同步监视器close状态,无法执行其中的代码,第二个线程也进入阻塞状态
4)第一个线程再次获取CPU,接着执行后续的代码;同步代码块执行完毕,释放锁open
5)第二个线程也再次获取cpu,来到了同步代码块,发现同步监视器open状态,拿到锁并且上锁,由阻塞状态进入就绪状态,再进入运行状态,重复第一个线程的处理过程(加锁)
强调:同步代码块中能发生CPU的切换吗?能!!! 但是后续的被执行的线程也无法执行同步代码块(因为锁仍旧close)
3:其他
1)多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块,其他线程无法访问其中的任何一个代码块
2)多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块, 但是没有锁住使用其他同步监视器的代码块,其他线程有机会访问其他同步监视器的代码块
【第二种:同步方法】
public class WindowThread extends Thread { static int ticket = 300; public WindowThread() { } public WindowThread(String name) { super(name); }
@Override
public void run() { while (true) { if (ticket == 0) { break; } else { buyTicket(); } }
} public static synchronized void buyTicket(){
//如果只有synchronized,锁住的只是当前this对象(相当于每一个都有300张票)
//如果加了static,锁住的就是方法。if(ticket >= 1) { System.out.println(Thread.currentThread().getName() + "窗口,卖出第" + ticket + "票"); ticket--; } }
}
总结:
1:
多线程在争抢资源,就要实现线程的同步(就要进行加锁,并且这个锁必须是共享的,必须是唯一的。
咱们的锁一般都是引用数据类型的。
目的:解决了线程安全问题。
2:关于同步方法
- 不要将run()定义为同步方法
- 非静态同步方法的同步监视器是this
静态同步方法的同步监视器是 类名.class 字节码信息对象 - 同步代码块的效率要高于同步方法
原因:同步方法是将线程挡在了方法的外部,而同步代码块锁将线程挡在了代码块的外部,但是却是方法的内部 - 同步方法的锁是this,一旦锁住一个方法,就锁住了所有的同步方法;同步代码块只是锁住使用该同步监视器的代码块,而没有锁住使用其他监视器的代码块
【第三种方式:Lock锁】
@Override //Lock锁
public void run() { Lock lock = new ReentrantLock(); //创建Lock锁对象 while (true) { lock.lock();//上锁 if (ticket >= 1) { System.out.println(getName() + "窗口,卖出第" + ticket + "票"); ticket--; } lock.unlock(); //解锁 if (ticket == 0) { break; } }
}
5.5线程通信:
package com.msb.test11;
/*** @author : msb-zhaoss*/
public class Product {//商品类//品牌private String brand;//名字private String name;//引入一个灯:true:红色 false 绿色boolean flag = false;//默认情况下没有商品 让生产者先生产 然后消费者再消费//setter,getter方法;public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getName() {return name;}public void setName(String name) {this.name = name;}//生产商品public synchronized void setProduct(String brand,String name){if(flag == true){//灯是红色,证明有商品,生产者不生产,等着消费者消费try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//灯是绿色的,就生产:this.setBrand(brand);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.setName(name);//将生产信息做一个打印:System.out.println("生产者生产了:" + this.getBrand() + "---" + this.getName());//生产完以后,灯变色:变成红色:flag = true;//告诉消费者赶紧来消费:notify();}//消费商品:public synchronized void getProduct(){if(!flag){//flag == false没有商品,等待生产者生产:try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//有商品,消费:System.out.println("消费者消费了:" + this.getBrand() + "---" + this.getName());//消费完:灯变色:flag = false;//通知生产者生产:notify();}
}
相关文章:

Java多线程介绍及使用指南
“多线程”:并发 要介绍线程,首先要区分开程序、进程和线程这三者的区别。 程序:具有一定功能的代码的集合,但是是静态的,没有启动运行 进程:启动运行的程序【资源的分配单位】 线程:进程中的…...

HarmonyOS 5.0应用开发——列表(List)
【高心星出品】 文章目录 列表(List)列表介绍列表布局设置主轴方向设置交叉轴方向 列表填充分组列表填充 滚动条位置设置滚动位置滚到监听 列表项侧滑 列表(List) 列表介绍 列表作为一种容器,会自动按其滚动方向排列…...
自动化电气行业的优势和劣势是什么
优势 市场需求广泛: 自动化电气技术广泛应用于电力系统、制造业、交通、农业等多个领域,随着智能化、数字化趋势的加强,其市场需求持续增长。在智能制造、智能电网等领域,自动化电气技术更是发挥着关键作用,推动了行业…...
第 42 章 - Go语言 设计模式
在Go语言中,设计模式是一种被广泛接受的解决常见问题的最佳实践。这些模式可以分为三类:创建型模式、结构型模式和行为型模式。下面我将结合案例以及源代码对这三种类型的设计模式进行详细讲解。 创建型模式 创建型模式主要关注对象的创建过程…...
【机器学习】---大语言模型
引言:开启大语言模型的奇幻旅程 近年来,人工智能(AI)领域正在经历一场前所未有的技术革命,而其中最耀眼的明星莫过于大语言模型(Large Language Models, LLMs)。这些模型,犹如现代科…...

挑战用React封装100个组件【002】
项目地址 https://github.com/hismeyy/react-component-100 组件描述 组件适用于需要展示图文信息的场景,比如产品介绍、用户卡片或任何带有标题、描述和可选图片的内容展示 样式展示 代码展示 InfoCard.tsx import ./InfoCard.cssinterface InfoCardProps {t…...

MarkDown-插入图片-图片url地址的生成获取方法
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、url地址是什么二、如何获取图片的url地址1.了解图床2.使用图床获取图片URL地址2.1进入网站后,点击右下角“Select Image.”按钮,即可…...

插值、拟合和回归分析的相关知识
目录 0 序言 1 分段线性插值 2 多项式插值 3 样条插值 4 最小二乘拟合 5 多元线性回归 0 序言 在生产实践和科学研究中,常常有这些问题: 插值问题:由实验或测量得到变量间的一批离散样点,要求得到变量之间的函数关系或得到样点之外的…...

【小白学机器学习42】进行多次抽样,样本的分布参数和总体的分布参数的关系
目录 1 进行多次抽样,样本的分布参数和总体的分布参数的关系 2 样本容量越大,多次抽样的样本的分布参数和总体的分布参数的关系 3 随着样本容量增大,多次抽样均值的 平均值,方差的变化 4 随着样本容量增大,多次抽…...
链动星海 质引未来|中信银行加码科技金融 “接力式”服务助力“新质生产力”释放
11月26日,第二届中国国际供应链促进博览会(以下简称链博会)在北京中国国际展览中心开幕。中信集团以“链动星海 质引未来”为主题,亮相先进制造链展区。此次布展由中信金控主办、中信银行承办,携手中信证券、中信建投证…...
黑马2024AI+JavaWeb开发入门Day02-JS-VUE飞书作业
视频地址:哔哩哔哩 讲义作业飞书地址:飞书 一、作业1 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge">&l…...

云计算基础-期末复习
第一章:云计算概论 一、云计算的定义与特征 1. 定义: 云计算是一种通过网络以按需、可扩展的方式获取计算资源和服务的模式。它将计算资源视为一种公用事业,用户可以根据需求动态获取和释放资源,而无需了解底层基础设施的细节。…...

Java GET请求 请求参数在Body中使用Json格式传参
业务需要调个三方接口 使用GET请求方式 但是!请求参数不在Query中,竟然在Body中,使用Json格式传参 在API调试工具里面可以调通 在java代码里,死活调不通 网上搜了搜,找到一个靠谱的,记录一下 import o…...

AI数据分析工具(一)
Looker Studio(谷歌)-免费 优点 免费使用:对于中小型企业和个人用户来说,没有任何费用压力,可以免费享受到数据可视化和报表创建的功能。与Google服务集成:特别适合使用Google产品生态的企业,…...

go结构体匿名“继承“方法冲突时继承优先顺序
在 Go 语言中,匿名字段(也称为嵌入字段)可以用来实现继承的效果。当你在一个结构体中匿名嵌入另一个结构体时,嵌入结构体的方法会被提升到外部结构体中。这意味着你可以直接通过外部结构体调用嵌入结构体的方法。 如果多个嵌入结…...
【049】基于51单片机语音录放【Proteus仿真+Keil程序+报告+原理图】
☆、设计硬件组成:51单片机最小系统ISD4004语音芯片LM386音频放大器喇叭LCD1602液晶显示按键控制LED灯。 1、本设计采用STC89C51/52、AT89C51/52、AT89S51/52作为主控芯片,LCD1602液晶显示屏实时显示; 2、系统具有两种模式:录音…...

《软件项目管理》期末-复习题及参考答案
(1)赶工一个任务时,你应该关注( C ) A. 尽可能多的任务 B. 非关键任务 C. 加速执行关键路径上的任务 D. 通过成本最低化加速执行任务 (2)下列哪个不是项目管理计划的一部分?&#x…...
milvus 通俗易懂原理
向量值如何生成的 Milvus 是一个开源的向量数据库,专门用于处理高维向量的存储、搜索和分析。向量值本身通常来自于某些机器学习或深度学习模型的输出,尤其是在自然语言处理(NLP)、计算机视觉(CV)、推荐系…...
什么是撞库、拖库和洗库?
“撞库”是黑客通过收集互联网已泄露的用户和密码信息,生成对应的字典表,尝试批量登陆其他网站后,得到一系列可以登录的用户。 很多用户在不同网站使用的是相同的帐号密码,因此黑客可以通过获取用户在A网站的账户从而尝试登录B网…...

安卓-碎片的使用入门
1.碎片(Fragment)是什么 Fragment是依赖于Activity的,不能独立存在的,是Activity界面中的一部分,可理解为模块化的Activity,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛. Fragment不能独立存在,必须…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...