java:多线程

多线程

在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;
	}
	
	@Override
	public 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方法而死亡

线程状态的切换

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/546530.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【每日刷题】Day15

【每日刷题】Day15 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; 目录 1. 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 2. 142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 3. 143. 重…

基于Python的卷积网络的车牌识别系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

CST电磁仿真的点/线/面设置操作【入门基础】

选择点/线/面 通过Pick功能选择点/线/面的方法 Modeling > Picks > Picks > Pick Points, Edges or Faces Pick是在模型上或任意空间中选择Point、Edge、Face的功能。利用Pick功能可以轻松获取模型的位置、尺寸等信息&#xff0c;也可以在执行Modeling和Result Han…

Xxl-job执行器自动注册不上的问题

今天新建的项目要部署xxl-job&#xff0c;之前部署过好多次&#xff0c;最近没怎么部署&#xff0c;生疏了。部署完之后&#xff0c;服务一直没有注册到执行器管理里面&#xff0c;找了半天也没找到原因&#xff0c;看数据库里的xxl_job_registry表也是一直有数据进来。 后来看…

46.HarmonyOS鸿蒙系统 App(ArkUI)网格布局

Grid(){GridItem(){Button(按钮1).fontSize(28)}.backgroundColor(Color.Blue)GridItem(){Text(数学).fontSize(28)}.backgroundColor(Color.Yellow)GridItem(){Text(语文).fontSize(28)}.backgroundColor(Color.Green)GridItem(){Text(英语).fontSize(28)}.backgroundColor(Co…

数据结构(算法)

总结&#xff0c;建议看EXCEL的《算法》页签&#xff0c;不然感觉有点乱 备注原理/步骤时间复杂度空间复杂度串的应用模式匹配简单/暴力O(mn) KMP  O(mn) 树的应用树哈夫曼树1、带权路径长度WPL 2、外部排序-最佳归并树1、哈夫曼树的度&#xff0c;只有0和m&#xff08;m叉…

七月审稿之提升模型效果的三大要素:prompt、数据质量、训练策略(含Reviewer2和PeerRead)​

前言 我带队的整个大模型项目团队超过40人了&#xff0c;分六个项目组&#xff0c;每个项目组都是全职带兼职&#xff0c;且都会每周确定任务/目标/计划&#xff0c;然后各项目组各自做任务拆解&#xff0c;有时同组内任务多时 则2-4人一组 方便并行和讨论&#xff0c;每周文档…

Normalizing Flows

需要学的是神经网络 f f f, 用于完成从source distribution (Pz)&#xff08;latent space&#xff0c;一般为高斯分布&#xff09;到 target distribution (Px) 的映射。 Normalizing Flows 是一种强大的生成模型&#xff0c;它通过学习一个可逆且易于计算的转换来将复杂的概…

(弟弟14)递归•按顺序打印一个整数的每一位

这里是目录哦 题目代码运行截图递归思路递归停止条件如何实现“按顺序”悟了✨加油&#x1f389; 题目 按顺序打印一个整数的每一位。 代码 #include<stdio.h> void Print(int n) {if (n > 9)//递归停止条件{Print(n / 10);//不断趋近递归停止条件}printf("%d…

代码随想录算法训练营Day56|LC583 两个字符串的删除操作LC72 编辑距离

一句话总结&#xff1a;看起来复杂&#xff0c;动规分析以后就比较简单。 原题链接&#xff1a;583 两个字符串的删除操作 本质就是求两个字符串的最短子序列的长度。已经做过&#xff0c;不再详解。 class Solution {public int minDistance(String word1, String word2) {/…

一文读懂自动化运维工具ansible及其使用

1. ansible简介 ansible是干什么的 ansible是目前最受运维欢迎的自动化运维工具&#xff0c;基于Python开发&#xff0c;集合了众多运维工具&#xff08;SaltStack puppet、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置、批量程序部署、批量运行命令…

麒麟服务器操作系统安装HTTP服务

往期好文&#xff1a;麒麟服务器操作系统安装TFTP服务 Hello&#xff0c;大家好啊&#xff01;今天我们将探讨如何在麒麟服务器操作系统上安装和配置HTTP服务&#xff0c;这是任何网络服务或应用的基础。无论你是想建立一个简单的网站&#xff0c;还是需要一个全功能的Web服务器…

wangzherongyao 2024.04.15

第一局&#xff1a;百里陪那只重置技能CD的辅助&#xff0c;对面有兰陵王&#xff0c;妲己&#xff0c;然后我补位廉颇被自己人和对面一阵嘲讽&#xff0c;真的不想说啥&#xff0c;对面盾山和妲己估计都没明白&#xff0c;我一只就能破他们队伍&#xff0c;所以看到没先出魔抗…

在Windows上配置VS Code GO语言开发环境

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

代码随想录阅读笔记-回溯【全排列】

题目 给定一个 没有重复 数字的序列&#xff0c;返回其所有可能的全排列。 示例 输入: [1,2,3]输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 思路 以[1,2,3]为例&#xff0c;抽象成树形结构如下&#xff1a; 回溯三部曲 1、递归函数参数 首先排列是有…

C++内存分布

C代码编译过程 预处理 宏定义展开、头文件展开、条件编译&#xff0c;这里并不会检查语法编译检查语法&#xff0c;将预处理后文件编译生成汇编文件汇编将汇编文件生成目标文件(二进制文件)链接将目标文件链接为可执行程序 进程的内存分布 程序运行起来(没有结束前)就是一个…

Java实现单点登录(SSO)详解:从理论到实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

亚信安慧AntDB:为安全加码

亚信安慧AntDB分布式数据库凭借平滑扩展、高可用性和低成本三大核心优势&#xff0c;在业界获得了极高的评价和认可。这些优点不仅为AntDB提供了巨大的市场发展潜力&#xff0c;也使其成为众多企业在数据管理上的首选解决方案。 AntDB的平滑扩展特性极大地提升了企业的灵活性和…

内网渗透-内网环境下的横向移动总结

内网环境下的横向移动总结 文章目录 内网环境下的横向移动总结前言横向移动威胁 威胁密码安全 威胁主机安全 威胁信息安全横向移动威胁的特点 利用psexec 利用psexec.exe工具msf中的psexec 利用windows服务 sc命令 1.与靶机建立ipc连接2.拷贝exe到主机系统上3.在靶机上创建一个…

EasyRecovery数据恢复软件2024百度云网盘下载链接

EasyRecovery数据恢复软件是一款功能强大的数据恢复工具&#xff0c;它能够帮助用户从各种存储设备中恢复丢失或误删除的文件数据。无论是由于意外删除、格式化、病毒攻击还是其他原因导致的数据丢失&#xff0c;EasyRecovery都能提供有效的解决方案。 该软件支持多种存储介质…