java:多线程

news2024/9/22 13:46:27

多线程

在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方法而死亡

线程状态的切换

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1,六步搭建AI智能客服机器人

现如今&#xff0c;智能客服机器人成为了企业提升服务效率、优化客户体验的重要工具。它不仅可以24小时不间断地为客户提供服务&#xff0c;还减少人工成本。那么&#xff0c;如何从零开始搭建一个AI智能客服机器人呢&#xff1f;本文将为您简要介绍几个关键步骤。 一、明确需求…

Mybatis generate xml 没有被覆盖

添加插件即可 <plugin type"org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>

51单片机学习笔记——点亮数码管、模块化编程

工作原理图 138译码器 G1、G2A、G2B为138译码器的使能端 P22、P23、P24为输入口控制Y0-Y7的动作&#xff0c;以二进制的算法 具体如下&#xff1a; P22 P23 P24 0 0 0 Y0为1其余为0 0 0 1 Y1为1其余为0 0 1 0 Y2…

【香橙派zero3蓝牙串口wifi配网教程】

【香橙派zero3蓝牙串口wifi配网教程】 1. 简介2. 准备工作2.1 zero3串口5开启2.2 硬件连接 3. 配网步骤3.1 连接串口3.2 打开串口终端3.3 运行配网脚本3.4 输入WiFi信息 4. 注意事项5. 总结 1. 简介 香橙派Zero3是一款基于H618处理器的单板计算机&#xff0c;具有丰富的接口和…

Itasca pfc3d/3dec/flac3d/massflow 9.0 授权

所有 Itasca 软件都建立在每个程序基础的共同元素层之上——无论程序使用何种数值方法或元素。因此&#xff0c;无论是使用 DEM 软件&#xff08;如 3DEC 或 PFC&#xff09;&#xff0c;还是使用 FLAC3D 等连续体软件&#xff0c;都会有许多流程、实用程序和功能是所有这些软件…

C++奇迹之旅:隐含的this指针

文章目录 &#x1f4dd;this指针&#x1f320; this指针的引出&#x1f309; this指针的特性&#x1f309;this指针存在哪里 &#x1f320;思考&#x1f320;C语言和C实现Stack的对比&#x1f309; C语言实现&#x1f309; C实现 &#x1f6a9;总结 &#x1f4dd;this指针 在C…

Redis入门到通关之Set实现点赞功能

文章目录 set 数据类型介绍不排序实现排序实现 set 数据类型介绍 Redis中的set类型是一组无序的字符串值。 set通过其独特的数据结构和丰富的命令提供了在存储和处理集合元素方面的一些非常有用的功能。下面列出了主要的set类型命令&#xff1a; SADD key member1 [member2]&a…

第十五届蓝桥杯 javaB组第三题

测试通过了90% 剩下10%不知道哪错了 思路&#xff1a;我想的是用map&#xff0c;k存第几个队列&#xff0c;value存每个子队列的长度&#xff0c;最后给value排序 第一个就最小的也就是是有效元素数量 考试只对了个案例&#xff0c;其它情况没测试。 复盘 回来后经过修改改…

openwrt局域网配置多个IP

在局域网配置过程中&#xff0c;若是DHCP服务器关闭&#xff0c;又忘记了配置的ip&#xff0c;将很难访问到路由器重新进行配置。这种情况可以在路由器出厂时做一个备用ip去避免。 1.配置 以下是备用ip的配置方法&#xff0c;以SKYLAB的SKW99 WIFI模组为例进行说明&#xff1…

如何应对MySQL单表数据量过大:垂直分表与水平分表策略解析

话接上回&#xff0c;单表最大数据建议两千万&#xff0c;那如果开发一个项目&#xff0c;预计注册量达到一个亿怎么办。 单表内放这么多数据&#xff0c;MYSQL底层B树的层级结构就可能会变得很高&#xff0c;磁盘io次数变多&#xff0c;性能会大幅度降低。所以考虑数据库分表…

设计模式-模板方法模式(TemplateMethod)

1. 概念 模板方法模式是一种行为设计模式&#xff0c;它在一个方法中定义算法的骨架&#xff0c;将一些步骤延迟到子类中实现。 2. 原理结构图 2.1 图 2.2 角色 抽象类&#xff08;Abstract Class&#xff09; 定义抽象的基本操作&#xff08;Primitive Operations&#xff…

Java+saas模式 智慧校园系统源码MySQL5.7+ elmentui前后端分离架构 让校园管理更高效的数字化平台系统源码

Javasaas模式 智慧校园系统源码MySQL5.7 elmentui前后端分离架构 让校园管理更高效的数字化平台系统源码 智慧校园是在数字通增强版基础上&#xff0c;研发的一套面向教育行业的数字化校园软件&#xff0c;其显著特点是集学校网站、协同办公、即时通讯、网络空间、移动办公于一…

接口自动化测试之调用excel实现接口数据依赖

背景 我们把接口的信息按照规定的格式都维护在excel文件中&#xff0c;通过代码实现调用excel&#xff0c;完成接口的自动化测试。这样&#xff0c;只需要负责人将主要逻辑写好之后&#xff0c;公司其他不会写代码的员工&#xff0c;也可以通过维护excel中的接口数据&#xff…

C语言 递归

递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;“从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&…

【算法练习】29:插入排序学习笔记

一、插入排序的算法思想 原理&#xff1a;将一个无序的数据序列逐步转化为有序序列。算法将待排序的数组分为两个部分已排序部分和未排序部分。 时间复杂度&#xff1a;插入排序的时间复杂度在最坏、平均和最好情况下的表现相同&#xff0c;均为 &#xff0c;其中 n 是待排序数…

OpenHarmony轻量系统开发【10】编写自己的软件包

10.1 添加第一个a_myparty软件包 打开鸿蒙系统的源码&#xff0c;可以看到有这么一个文件夹&#xff1a;third_party。里面存放的是第三方的代码。 点开我们可以看到有很多第三方代码&#xff1a; 后续我们如果需要往系统中添加、移植任何开源代码&#xff0c;都可以添加到这个…

ChatGPT们写高考作文会发生什么?

2023年的高考结束了&#xff0c;今年共有1291万考生参加了高考了&#xff0c;再次创造了历史参考人数之最。在高考中&#xff0c;要说什么最引人讨论&#xff0c;那高考作文当仁不让啊。今年随着ChatGPT的爆火&#xff0c;可谓是给文字工作者带来一大助力&#xff0c;如果让AI来…

做测试,不会接口测试怎么能行?

接口测试的测试点 功能测试&#xff1a;单接口功能、业务场景功能 性能测试&#xff1a;响应时长、错误率、吞吐量、服务器资源使用率 安全测试&#xff1a;敏感数据是否加密、SQL注入、其他 本篇文章以接口功能测试为主。 接口用例设计方法&#xff1a; 单接口测试用例设…

在网上打印资料多少钱一张

随着互联网的普及和线上服务的完善&#xff0c;越来越多的人选择在网上打印资料。这种方式不仅方便快捷&#xff0c;而且通常价格更为透明和实惠。那么&#xff0c;在网上打印资料到底多少钱一张呢&#xff1f;这主要取决于您选择的打印平台、纸张规格、打印质量以及打印数量等…

SpringBoot的旅游管理系统+论文+ppt+免费远程调试

项目介绍: 基于SpringBoot旅游网站 旅游管理系统 本旅游管理系统采用的数据库是Mysql&#xff0c;使用SpringBoot框架开发。在设计过程中&#xff0c;充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 &#xff08;1&…