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不能独立存在,必须…...
华为IPD流程学习之——深入解读123页华为IPD流程体系设计方法论PPT
该方案全面介绍了华为IPD流程体系设计方法论,包括流程体系建设的背景、理念、架构、核心特征、构建模型、与组织和战略的关系、运营机制、数字化转型以及流程管理组织等内容,旨在为企业提供一套系统的流程体系建设指导,以提升运营效率、质量和…...
DriveMLLM:一个专为自动驾驶空间理解任务设计的大规模基准数据集
2024-11-20, 由武汉大学、中国科学院自动化研究所、悉尼科技大学、牛津大学等合创建了DriveMLLM数据集,该数据集是自动驾驶领域首个专为评估多模态大型语言模型(MLLMs)空间理解能力而设计的基准,对于推动自动驾驶技术的…...
高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)
引言 在现代iOS应用开发中,处理大规模数据是一个常见的挑战。尤其实在直播项目中,礼物面板作为展示用户互动的重要部分,通常需要实时显示海量的礼物数据。这些数据不仅涉及到不同的区域、主播的动态差异,还需要保证高效的加载与渲…...
python的函数与递归
需求: 编写一个函数,计算斐波那契数列的第 N 项,并使用递归实现。 为了计算斐波那契数列的第 N 项,可以使用递归方法。斐波那契数列的定义是: F(0) 0 F(1) 1 对于 n > 2,F(n) F(n-1) F(n-2)…...
RabbitMQ学习-Seven
再SpringBoot中使用MQ 1.创建SpringBoot项目 除了我们平常使用的一些工具依赖,还需要选择这个Spring for RabbitMQ依赖 2.需要在application.yml文件中进行配置 server:port :9090 spring:application:name:producerrabbitmq:host: 你的主机名port: 5672virtual-…...
中科亿海微SoM模组——波控处理软硬一体解决方案
本文介绍的波控处理软硬一体解决方案主要是面向相控阵天线控制领域,波控处理通过控制不同天线组件的幅相来调整天线波束的方向和增益,实现高精度角度控制和高增益。本方案由波控处理板、波控处理控制软件算法和上位机软件共同构成。波控处理SoM模组原型样…...
开源法律、政策和实践
#一切皆可开源# 木兰社区对《Open Source Law,Policy and Practice 》这本书的第二版进行了翻译,并发布在了gitee上。这本书是对开源文化、开源政策、法律的全面介绍。目录如下: 1 Open Source as Philosophy,Methodology,and CommerceUsing Law with …...
【计算视觉算法与应用】金字塔,下采样Gaussian Pyramid. 上采用 Laplacian Pyramid (code: py)
金字塔(Pyramid)在图像处理中主要用于多尺度分析和图像压缩。常见的图像金字塔有两种: 高斯金字塔(Gaussian Pyramid):用于下采样图像,生成分辨率逐渐降低的图像序列。拉普拉斯金字塔ÿ…...
基于BERT的语义分析实现
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
DNS查询工具
DNS查询工具是用于查询和获取域名相关信息的工具。通过这些工具,您可以获取到诸如IP地址、邮件服务器以及域名服务器等信息,这对于排查问题、设置域名配置以及确保网站正常运行都非常重要。 以下是五款常用的DNS记录查询工具: MxToolbox MxTo…...
科技进步法/seo关键词报价查询
题目:原题链接(简单) 解法时间复杂度空间复杂度执行用时Ans 1 (Python)O(N2)O(N^2)O(N2)O(1)O(1)O(1)328ms (29.32%)Ans 2 (Python)O(N)O(N)O(N)O(N)O(N)O(N)168ms (99.58%)Ans 3 (Python)LeetCode的Python执行用时随缘,只要时间…...
南澳做网站/晨阳seo服务
北京作为租赁需求旺盛的城市之一,未来五年计划供地1300公顷,建设租赁住房50万套。与往年“划拨”、“协议租赁”、“竞配建”等租赁用地供应方式不同,这50万套租赁住房,将主要通过集体建设用地安排。此外,北京还引入“…...
深圳定制型网站建设/seo整站优化新站快速排名
1 被动模式 zabbix默认采用被动模式。就是agent等待server采集数据。 在items中,type为zabbix agent就是指被动模式。 流程为:agent周期性收集数据,server打开一个tcp高位端口,去连接agnet的10050监听端口,请求数据&am…...
阅读转发网站那些做的比较好/天津网站快速排名提升
板子,全是板子 该模板目前行数(Markdown下包括文字):4799 行 更新日志(从2018.11.19开始) 2019.04.05 : 更新了数据结构->左偏树 2019.04.02 : 更新了数据结构->平衡树->WBLT 2019.03.26 : 更新了…...
怎么做58同城网站/中国国家培训网官网入口
Rebex ZIP for .NET加密或提取ZIP档案变得容易 Rebex ZIP 是一个用于 .NET 的 ZIP、UnZIP、Deflate、ZLIB 和 GZIP 压缩库。它使创建、修改、加密或提取 ZIP 档案变得容易。 Rebex ZIP 功能 文件和目录操作变得简单 Rebex ZIP for .NET从文件系统或内存中添加或提取文件。重命名…...
建设部门的网站/宁德市人民政府
在文章http://blog.csdn.net/u013063153/article/details/73611549写到了非HA集群的搭建。 现利用Zookeeper做HA,搭建HDFS集群。需要修改之前的配置。 1.进入etc/hadoop/目录,干掉masters文件(此文件是存放Secondary NameNode的) cd etc/hadoop/ rm …...