java:多线程

news/2024/5/3 4:11:55

多线程

在java程序中同时执行多个线程,每个线程独立执行不同的任务.

可以提高程序的性能和资源利用率,增加程序的并发性.

多线程的作用

1,提高程序性能

可以将一个任务分解成多个子任务并行处理,从而提高程序的运行速度

2,提高资源利用率

可以更好地利用CPU资源,提高CPU的利用率

3,提高程序的并发性

可以使程序同时处理多个任务,提高程序的并发处理能力

创建线程的方式

1,继承Thread类

重写run()方法定义线程的执行逻辑,创建该类实例

调用start()方法启动线程

优点:编码简单
缺点:继承Thread类就无法继承其他类不利于功能扩展
run方法中异常只能捕获不能抛出
MyThread mythread = new MyThread();
mythread.start();
//调用start()方法启动线程(启动后执行run()方法)class MyThread extends Thread{//重写run方法
}
2,实现Runnable接口

创建一个实现了Runnable接口的类,实现其run()方法,定义线程的执行逻辑.

创建Thread类实例,将之前实现的对象作为参数传递给Thread的构造函数

最后调用Thread实例的start()方法来启动线程

优点:只需实现接口,仍可以继承和实现,扩展性强
缺点:需要多一个Runnable对象
run方法中异常只能捕获不能抛出
MyRunnable myRunnable = new MyRunnable();
//创建接口实现类对象
Thread thread = new Thread(myRunnable);
//创建线程对象
thread.start();
//开启线程//任务类
class MyRunnable implements Runnable{//重写run()方法
}
3,实现Callable接口:

创建一个实现了Callable接口的类,重写接口中的call抽象方法,定义任务并交给Thread类对象完成.

优点:扩展性强(同上),可以获取线程执行结果(有返回值)
缺点:编码复杂
异常可以抛出
Mycallable call = new Mycallable();
//创建任务类对象
FutureTask<String> task = new FutureTask<>(callable);
//继承于Runnable和Future
//定义桥梁
Thread thread = new Thread(task);
//创建线程对象
thread.start();
//开启线程
.............
task.get().var;
//具备阻塞效果,会让task对应的线程先执行完再执行下面的代码
//获取线程执行完毕返回的结果值class MyCallable implements Callable<String>{//泛型里是call方法的返回值类型//重写call()方法
}
public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);//isCancelled:如果此任务在正常完成之前被取消,则返回true。boolean isCancelled();//isDone:如果此任务完成,则返回true。boolean isDone();//get:等待任务完成,然后返回其结果。是一个阻塞方法V get() throws InterruptedException, ExecutionException;//get(long timeout, TimeUnit unit):等待任务完成,然后返回其结果。//如果在指定时间内,还没获取到结果,就直接返回null。V get(long timeout, TimeUnit unit)//TimeUnit是一个枚举类,对象为时间类型,这里传入前面timeout的时间类型throws InterruptedException, ExecutionException, TimeoutException;
}
/*cancel:用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
*参数mayInterruptIfRunning表示是否取消正在执行却没有执行完毕的任务,如果设置true,
*则表示可以取消正在执行过程中的任务。
*如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,
*若mayInterruptIfRunning设置为false,不会取消恩物,返回false;如果任务还没有执行则无论*mayInterruptIfRunning为true还是false,肯定返回true。
*如果任务已经完成则无论mayInterruptIfRunning为true还是false,一定会返回false;*/

Thread类

一个表示线程的类

提供了一系列的方法用于线程操作

构造器

public Thread(String name)
//可以为当前线程指定名称
public Thread(Runnable target)
//封装一个Runnable对象为线程对象
public Thread(Runnable target,String name)
//封装Runnable对象成为线程对象,并指定线程名称

常用方法

public void run()
//线程的任务方法
public void start()
//启动线程
public String getName()
//获取当前线程的名称,线程名称默认为Thread-索引
public void setName(String name)
//为线程设置名称
public static Thread currentThread()
//获取当前执行的线程对象
public static void sleep(long time)
//让当前执行的线程休眠多少毫秒后继续执行
public final void join()....
//让调用当前方法的线程先执行完
//具备阻塞作用

线程安全问题

java中的线程安全问题是指多个线程同时访问共享资源时进行操作

如果执行顺序不确定,可能会导致数据不一致或者程序出错的问题

Account account1;
new Thread(new AccountThread(account1),"小明").start()
//线程1
new Thread(new AccountThread(account1),"小红").start()
//线程2
//多个线程对同一个对象修改class AccountThread implements Runnable{private Account account;public AccountThread(Account account){this.account = account;}@Overridepublic void run(){//线程启东时执行对对象操作account.drawMoney(100000);}
}class Account{}

如上所示,因为对象在堆内存中被共享,所以出现多个线程对同一个对象执行操作时,就会出现对象的线程安全问题.

线程不安全的原因

1、线程是抢占式执行

线程的抢占式执行就是在一个线程的执行过程中,另一个更优先的进程会抢占当前线程执行的任务,当前线程就会被迫中断,这是引发线程不安全的根本原因,但是线程的调度是随机的,这是由系统决定的,我们无法改变。

2、多线程共享同一变量

多线程共享同一变量如果只是读操作就不会引发线程安全问题,但是如果多线程都修改同一变量就会引发安全问题,就是引例当中的情况,因为修改操作不是原子性,需要多步完成就有可能发生线程抢占导致中断。

3、对变量的操作不是原子性

操作原子性也就是操作能一步完成,但是修改变量的操作就可以分为:将变量从内存加载到寄存器(load) 、修改变量(update)、把寄存器的值加载回内存(save)

例如是自增操作: 假设内存中的变量的初始值为0,t1就先把0加载到寄存器,但是t2进行抢占,也从内存中把0加载到寄存器然后自增为1,然后将1加载回内存,然后t1再自增为1,再将1加载回内存,按道理两次自增应该为2,但是由于线程的抢占以及自增操作是非原子的就会出现上述情况。

4、内存可见性

变量通常存放在内存中,线程对变量操作需要首先从内存中拿出到寄存器,但是一个线程频繁进行读操作,就可能会直接从寄存器上读,不再进入内存这就引发线程不安全,因为线程得不到内存中变量的最新值。

5、指令重排序

指令重排序是编译器的优化操作来提高代码运行的效率,但是对于多线程在进行指令重排序时就可能会出现错误引发线程安全问题。

线程同步-同步代码块

多个线程间协调工作,保证线程安全和正确性的技术

在多线程环境下,如果多个线程同时访问共享资源,可能会导致数据的不一致,而线程同步就可以解决线程安全问题.

线程同步和加锁是密不可分的,加锁是线程同步的一种手段,用于实现多个线程对共享资源的访问控制.

死锁:

当线程间都在互相等待对方的资源时,就会出现死锁


注意事项

1,在线程同步中,当一个线程需要访问共享资源时,会先尝试获取锁,如果锁没有被其他线程占用,则获取成功

2,在获取锁后,该线程就可以访问共享资源了

3,其他线程在访问共享资源之前,必须等待该线程释放锁之后才能获取锁并继续访问共享资源.

4,通过加锁,可以保证同一时间只有一个线程能够访问共享资源,从而避免多个线程同时修改共享资源导致的数据不一致或者冲突问题.加锁是实现线程安全的重要手段之一.

同步代码块(锁对象)

在一段代码前后使用synchronized关键字包裹,来实现线程同步的功能.

synchronized(需要上锁的对象){//代码块
}

对于同时执行的线程来说,锁对象必须为同一个.

使用同步代码块的作用是确保在某个时刻只有一个线程能执行这段代码,从而避免多个线程同时访问共享资源时出现的并发问题.

同步方法

使用synchronized关键字修饰方法,使得在多线程环境下,只有一个线程能执行这个方法.

修饰符 synchronized 返回值类型 方法名称(参数){操作共享资源的代码
}
同步方法的底层原理

1,底层原理锁住整个方法代码范围的隐式锁对象

如果方法为实例方法:同步方法默认使用this作为锁对象

如果方法为静态方法:同步方法默认使用类名.class作为锁对象

锁对象详解

1,对象锁

锁住对象,不同实例的锁互相不影响

synchronized关键字在普通方法上

synchronized(this)

锁住当前线程传入的对象,其他对象不受影响
2,类锁

synchronized关键字加在静态方法上类锁

synchronized(类.class),等同于(synchronized(object obj))

obj为静态对象

锁定所有对象,因为所以对象共享同一个静态方法

Lock锁

jdk5开始提供的一个新的锁定操作,可以创建锁对象进行加锁和解锁,更灵活也更强大

Lock是一个接口,不能实例化但他的实现类ReentrantLock可以构建Lock锁对象

锁住lock和unlock之间的代码,类似于同步代码块

构造器
public ReentrantLock()
//获得Lock锁的实现类对象
常用方法
void lock()//获得锁
void unlock()//释放锁

线程通信

多个线程之间交互和通信的方式

通过Object类中的wait()和notify()方法实现多个线程之间的等待-唤醒操作,实现线程的同步和通信,可以使用这种方式来实现生产者-消费者模式,等待-通知模式等常见的多线程编程模式

常用方法
void wait()
//当前线程等待,直到另一个线程调用notify()或者notifyAll()唤醒自己
void notify()
//唤醒正在等待对象监视器(锁对象)的单个线程
void notifyAll()
//唤醒正在等待对象监视器(锁对象)的所有线程

wait和notify必须使用同一把锁调用

wait方法会释放锁,但notify方法不会

线程池

一种线程复用技术,可以减少线程的创建和销毁次数,提高程序的性能和响应速度.

维护了一组工作线程,接受任务并执行,任务执行完毕后,工作线程可以再次被复用,从而避免了每次执行任务都创建新线程的开销.

线程池的优势

1,提高程序性能

重复使用线程,减少创建和销毁线程所消耗的时间和资源,提高了程序性能.

2,提供更好的控制

线程池可以控制线程的创建和销毁,可以控制线程的数量,防止过多的线程导致系统资源的浪费.

3,提高响应速度

线程池可以将任务加入队列,等待程序的执行,从而避免阻塞线程,提高响应速度.

线程池的创建和使用

Executors工厂类:提供了一些静态方法创建不同类型的线程池对象(不建议使用)
public static ExecutorService newFixedThreadPool(int nThreads)
//创建固定线程数量(nThreads)的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程来替代它
public static ExecutorService newSingleThreadExecutor()
//创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新的线程
public static ExecutorService newCachedThreadPool()
//线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60%会被回收掉
public static ScheduleExecutorService newScheduledThreadPool(int corePoolSize)
//创建线程池,可以实现在给定的延迟后运行任务或者定期执行任务
常用方法
void execute(Runnable command)
//执行任务/命令,没有返回值,一般用来执行Runnable任务
Future<T> submit(Callable<T> task)
//执行任务,返回未来任务对象获取线程结果,一般拿来执行Callable任务
void shutdown()
//等任务执行完毕后关闭线程池
//一般不关闭
List<Runnable> shutdownNow()
//立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
自定义线程池对象(重点)

java提供了一个代表线程池的接口:ExecutorService

可以使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象

ThreadPoolExecutor构造器
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQuene<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler
)

corePoolSize:指定线程池的核心线程数量

maximumPoolSize:指定线程池的最大线程数量(核心+临时)

keepAliveTime:指定临时线程的存活时间

unit:指定临时线程存活的时间单位(秒,分,时,天)

workQueue:指定线程池的任务队列,阻塞队列(BlockingQueue<>的实现类对象)

threadFactory:指定线程池的线程工厂(创建线程的地方)

handler:指定线程池的任务拒绝策略(任务队列满时,新任务来怎么处理)

任务拒绝策略
ThreadPoolExecutor.AbortPolicy
//丢弃任务并抛出RejectedExecutionException异常(默认)
ThreadPoolExecutor.DiscardPolicy
//丢弃任务,但不抛出异常(不推荐)
ThreadPoolExecutor.DiscardOldestPolicy
//抛弃队列中等待最久的任务,然后把任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy
//由主线程负责调用任务的run()方法从而绕过线程池执行
//只会执行超出队列的线程

并发和并行

正在运行的程序(软件)就是一个独立的进程.

线程属于进程,一个进程中可以同时运行多个线程

进程中的线程是并发和并行执行的.

并发

进程中的线程由CPU调度,但cpu同时只能处理一部分线程,为了保证全部线程能向前执行,会轮询每个线程进行服务.

虽然我们感觉线程在同时执行,但实际上是cpu快速的在线程之间切换.这就是并发

并行

在同一个时刻上,多个线程被CPU同时调度执行(多核处理)

线程的生命周期

线程状态

Thread类中有State枚举类

其中枚举项即为以下状态

NEW(新建)
//线程刚被启动,但是并未启动
Runnable(可运行)
//线程已经调用了start(),等待CPU调度
Blocked(锁阻塞)
//线程在执行的时候未竞争到锁对象,则进入Blocked状态
Waiting(无限等待)
//一个线程进入Waiting状态,另一个线程调用notify()或者notifyAll()方法才能够唤醒
Timed Waiting(计时等待)
//在时间抵达之前等待,一般由sleep或者wait设置超时参数得到
Terminated(被终止)
//因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡

线程状态的切换

请添加图片描述


http://www.mrgr.cn/p/27438210

相关文章

ThinkPHP 5.0.23 远程代码执行漏洞

漏洞检测工具 ThinkPHP 5.0 RCE检测工具 https://github.com/Lotus6/ThinkphpGUI/releases 解决办法 https://blog.csdn.net/sjsjshhs134654/article/details/131305418如果这篇文章对你有用,可以关注本人微信公众号获取更多ヽ(^ω^)ノ ~

【计算机毕业设计】点餐平台网站——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

Visual Components软件为您带来的价值 衡祖仿真

Visual Components具备丰富的3D仿真功能、快速建模能力、定制化应用程序逻辑和大量预定义组件库等多种特点,为自动化设备制造商、整合商、制造型公司提供简单、 快速和的设计方式,可以有效提高生产效率,并优化资源分配,避免制造过程中各种不可控事件带来的影响。Visual Com…

按吉他弦手不再疼痛的真正奧義!!~(姿勢

學吉他最痛苦的事情莫過”手痛”,網路上分析關於手痛的解決方式有好幾篇,不外乎就是…降弦、靠琴桁按弦、換弦的材質、換細弦、調弦距…等,我就不再多廢話講跟別人差不多的東西。但我發現有一件事情是很少老師提及的,那就是關於按弦的”姿勢”,其實姿勢/角度跟按弦的聲音好…

代码随想录图论

1. 所有可能的路径 class Solution:def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:def dfs(graph, result, path, root): #result 返回结果, path记录路径, root记录遍历到了第几个节点if root len(graph) - 1: #如果遍历到最后…

上海法院诉讼自如电费欺诈维权一审胜诉 All In One

上海法院诉讼自如电费欺诈维权一审胜诉 All In One 依法维权上海法院诉讼自如电费欺诈维权一审胜诉 All In One依法维权作为一个法律新手,从阅读学习有关法律法规,收集证据,法律咨询, 立案申请,法院受理,整理提交证据, 法院开庭,法庭辩论, 历时几个月的努力和耐心等待,最…

5 步轻松上手,教你从 0 到 1 落地 Jmeter 接口自动化脚本!

Jmeter是进行接口测试的一款非常主流的工具,但绝大部分测试工程师,对于Jmeter接口测试脚本整理都是一知半解的。今天这篇文章,就以一个金融项目中接口为例,通过简单5步,教大家如何0代码编写Jmeter接口自动化脚本! 0、金融项目接口 1、登录接口信息2、新增投资项目接口信息…

sectigo怎么样,有哪些优势

SSL证书是Sectigo的核心服务之一。SSL&#xff0c;全称为安全套接层&#xff08;Secure Sockets Layer&#xff09;&#xff0c;它为网络通信提供加密服务&#xff0c;保障数据在传输过程中的安全性。在数字化交易和信息交换日益增长的当今时代&#xff0c;SSL证书几乎成为了所…

总结SQL相对常用的几个字符函数

目录 字符的截取 substr() trim()、ltrim()、rtrim() 字符串的拼接 ||、 字符的大小写转换 upper(column_name):大写 lower(column_name):小写 字符替换 replace() 搜索字符 instr(column_name, substring_to_find,start,n_appearence) charindex(substring_to_fi…

从IP网络到命名数据网络(NDN)简介

从IP网络到命名数据网络(NDN)简介由于科研需求,学习了解一下NDN相关技术。 由于本人的排版太菜了,本文还是由GPT-4排版+润色。 本文章用双拼完成,还在学习中。传统IP网络面临的挑战 IP网络的诞生 IP网络的概念最初是在20世纪70年代由美国国防部高级研究计划局(DARPA)提出…

【使用PADS软件将PCB由N(N2)层板改为2层板】

最近接触PADS软件比较多,相比Altium Designer来说,PADS软件操作更为繁琐,使用中遇到的一些问题,常常百度很久之后也找不到确切结果。。。 此文章记录将PCB由N(N>2)层板改为2层板的操作过程,实践无误,特此总结,希望对遇到相同困惑的朋友有所帮助~ ps:笔者使用的软件…

揭秘网红主播美颜工具:探秘美颜SDK的技术奥秘

在如今的网络直播平台上&#xff0c;越来越多的主播通过美颜工具来提升自己的形象&#xff0c;吸引更多的粉丝和观众。美颜技术的不断发展使得主播们能够在镜头前展现出更加完美的容颜&#xff0c;让观众眼前一亮。 一、美颜SDK的概念 美颜SDK&#xff0c;即美颜软件开发工具…

元宇宙VR虚拟线上展馆满足企业快速布展的需要

想要拥有一个VR线上虚拟展馆&#xff0c;展现您的城市风采或企业特色吗? 相比实体展馆搭建&#xff0c;VR线上虚拟展馆投入资金少&#xff0c;回报周期短&#xff0c;只需几个月的时间&#xff0c;您就能开始资金回笼。那么一个VR线上虚拟展馆多少钱呢? 深圳VR公司华锐视点基…

51单片机入门_江协科技_29~30_OB记录的自学笔记_DS18B20温度传感器

29. DS18B20温度传感器 29.1. DS18B20介绍 •DS18B20是一种常见的数字温度传感器&#xff0c;其控制命令和数据都是以数字信号的方式输入输出&#xff0c;相比较于模拟温度传感器&#xff0c;具有功能强大、硬件简单、易扩展、抗干扰性强等特点 •测温范围&#xff1a;-55C 到 …

ImageJ软件使用教程(三):目标计数

目录多点工具法阀值分割法二值化填充分割自动计数显示结果总结参考资料 本文以钢筋计数为例,讲解一下如何使用ImageJ软件进行计数,这里只介绍两种方法:多点工具法 阀值分割法钢筋计数是我接触的第一个视觉项目,虽然项目最后不了了之,但作为我机器视觉的开荒项目还是很有纪…

软考135-上午题-【软件工程】-软件配置管理

备注&#xff1a; 该部分考题内容在教材中找不到。直接背题目 一、配置数据库 配置数据库可以分为以下三类&#xff1a; (1) 开发库 专供开发人员使用&#xff0c;其中的信息可能做频繁修改&#xff0c;对其控制相当宽松 (2) 受控库 在生存期某一阶段工作结束时发布的阶段产…

NewTomNNT意大利杰诺牙科口腔CT图像分析处理软件

NNT软件的先进功能可以覆盖多个医学专业,特殊的重建窗口响应每个部门的不同需求。所有检查都完全兼容 DICOM 格式:它们可以通过 NNT 查看器共享或以 1:1 的比例打印。与 NewTom 采用的现代系统的出色连接性和集成性。工作流程、临床和诊断活动变得更加容易和高效。简单的 3D …

实验二 需求分析

五、绘制用例图 绘制小型网上书店顶层用例图:六、绘制类图 绘制用户登录模块类图:七、绘制状态图 绘制用户登录模块状态图:八、绘制顺序图 绘制“登录注册”模块的顺序图:九、绘制活动图 绘制“登录注册”模块的活动图:十、实验中遇到的问题及解决方法 1.问题:关于用例图之间的…

游戏前摇后摇Q闪E闪QE闪QA等操作

备注&#xff1a;未经博主允许禁止转载 个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_w…

(内含福利)Meta 发布新开源模型 Llama 3;华为 Pura 70 系列一分钟售罄丨 RTE 开发者日报 Vol.188

最新人工智能模型、华为 Pura 70、月之暗面 Kimi 开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real Time Engagement) 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章…