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;}@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.mzph.cn/news/819154.shtml

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

相关文章

如何实现Windows RDP 远程桌面异地跨网连接

Windows RDP远程桌面的应用非常广泛。远程桌面协议(RDP)是一个多通道(multi-channel)的协议&#xff0c;让使用者(所在计算机称为用户端或本地计算机)连上提供微软终端机服务的计算机(称为服务端或远程计算机)。大部分的Windows版本都有用户端所需软件&#xff0c;有些其他操作…

Python Flask Web 框架-API接口开发_4

一、1、安装 Falsk 当前用户安装 pip3 install --user Flask 确认安装成功&#xff1a; 进入python交互模式看下Flask的介绍和版本&#xff1a; $ python3>>> import flask >>> print(flask.__doc__)flask~~~~~A microframework based on Werkzeug. Its …

病毒繁殖-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第52讲。 病毒繁殖&#xf…

数据结构与算法——23.哈希冲突及其解决方法

这篇文章我们主要讲一下哈希冲突及其解决方法。 目录 1.哈希冲突的介绍 2.解决哈希冲突的方法 2.1 开放地址法 2.1.1 线性探测法 2.1.2 平方探测法&#xff08;二次探测法&#xff09; 2.2再哈希法 2.3链地址法 2.4建立公共溢出区 3.总结 1.哈希冲突的介绍 首先&…

vue快速入门(二十五)本地存储与初始化使用

注释很详细&#xff0c;直接上代码 上一篇 新增内容 本地获取数据数据存储到本地 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial…

【浪漫 罗盘时钟 Js、css实现(附源代码) 美化版本】,前端面试必问的HashMap

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

WebKit简介及工作流程

文章目录 一、WebKit简介二、WebKit结构三、Webkit工作流程四、WebKit常见问题五、WebKit优点六、相关链接 一、WebKit简介 WebKit是一个开源的浏览器引擎&#xff0c;它的起源可以追溯到2001年&#xff0c;当时苹果公司推出了其首款基于Unix的操作系统Mac OS X。在2002年&…

最短网络kruskal算法

题目描述 农民约翰被选为他们镇的镇长&#xff01;他其中一个竞选承诺就是在镇上建立起互联网&#xff0c;并连接到所有的农场。当然&#xff0c;他需要你的帮助。约翰已经给他的农场安排了一条高速的网络线路&#xff0c;他想把这条线路共享给其他农场。为了用最小的消费&…

【Java开发指南 | 第七篇】静态变量生命周期、初始化时机及静态变量相关性质

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 生命周期初始化时机常量和静态变量的区别静态变量相关性质静态变量的访问修饰符静态变量的线程安全性静态变量的命名规范 生命周期 生命周期&#xff08;Lifecycle&#xff09;是指一个对象从创建到销毁的整个…

光电传感器的工作原理简介

光电传感器是一种利用光电效应将光信号转换为电信号的传感器。 工作原理 光照射&#xff1a;光电传感器通过光源&#xff08;如LED或激光&#xff09;照射在其表面。 光电转换&#xff1a;光线与传感器材料发生光电反应&#xff0c;产生电信号。这种转换过程涉及到光子与电子的…

Albumentations库:为自定义数据进行数据增强

点击下方卡片&#xff0c;关注“小白玩转Python”公众号 为什么在深度学习中使用增强&#xff1f; 深度学习和计算机视觉中的增强已经成为至关重要的几个原因。首先&#xff0c;它丰富了训练数据集&#xff0c;使模型能够从更多样化的示例中学习&#xff0c;这在标记数据有限时…

C++修炼之路之list--C++中的双向循环链表

目录 前言 一&#xff1a;正式之前先回顾数据结构中的双向循环链表 二&#xff1a;list的简介 三&#xff1a;STL中list常用接口函数的介绍及使用 1.构造函数接口 2.list迭代器 范围for 3.数据的修改接口函数 4.list容量操作函数 5.list的迭代器失效 6.演示代码和测…

【深度剖析】曾经让人无法理解的事件循环,前端学习路线

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

Mybatispuls-自动填充

一&#xff0c; 创建时间 . 修改时间! 这些个操作都是自动化完成的,我们不希望手动更新! 阿里巴巴开发手册:所有的数据库表:gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化! 二&#xff0c;如何实现 1&#xff0c;编写实体类&#xff08;字段属性上添加注解…

ubuntu 23.10.1 mysql 安装

注&#xff1a;请进入root用户模式下操作&#xff0c;若没有&#xff0c;输入命令前加上sudo 1、更新软件包列表 apt update2、安装最新版的Mysql服务器 apt install mysql-server -y如果不加-y 会在安装过程中&#xff0c;系统将提示你设置MySQL的root密码。确保密码足够强…

基于直方图的图像阈值计算和分割算法FPGA实现,包含tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 VIVADO2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1ps // // Company: // Engineer: // // Design Name: // …

【御控物联】Java JSON结构转换(1):对象To对象——键值互换

文章目录 一、JSON是什么&#xff1f;二、JSON结构转换是什么&#xff1f;三、核心构件之转换映射四、案例之《JSON对象 To JSON对象》五、代码实现六、在线转换工具七、技术资料 一、JSON是什么&#xff1f; Json&#xff08;JavaScript Object Notation&#xff09;产生于20…

Leetcode876_链表的中间结点

1.leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5…

类与对象(中)

目录 赋值运算符重载 运算符重载 赋值运算符重载 前置和后置重载 日期类的实现&#xff08;前置后置也在里面&#xff09; const 成员 ​编辑 取地址及const取地址操作符重载 赋值运算符重载 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载…

【python】python天气气候数据抓取分析可视化(源码+数据+可视化+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…