单线程与多线程

article/2025/10/2 17:48:46

目录

多线程的创建 

方式一:继承Thread类

方式二:实现Runnablel接口

方式三:JDK5.0新增Callable接口

Thread的常用方法

线程安全

线程同步

线程通信

线程池【重点】

定时器

补充知识:并发、并行

生命周期


什么是线程?

  • 线程(Thread)是一个程序内部的一条执行路径。
  • 启动程序执行之后,main方法的执行其实就是一条单独的执行路径。
  • 程序中如果只有一条执行路径,那么这个程序就是单线程。

 多线程是什么?

  • 多线程是指从软硬件上实现多条执行流程的技术
  • 消息通信、淘宝、京东系统都离不开多线程技术

多线程的创建 

方式一:继承Thread类

Thread类

Java是通过java.lang.Thread类来代表线程。

按照面向对象的思想,Thread类应该提供了实现多线程的方式。

继承Thread类

  1. 定义一个子类MyThread继承线程类java.long.Thread,重写run()方法
  2. 创建一个MyThread类对象
  3. 调用线程对象的start()方法启动线程(启动后还是执行run方法)

优缺点:

  • 优点:编程简单
  • 缺点:线程类已经继承Thread,无法继承其他类,不利于扩展

1.为什么不直接调用run方法,而是调用start启动线程

  • 直接调用run方法会当成普通方法执行,此时相当于还是方向陈执行。
  • 只有调用start方法才是启动一个新的线程执行

2.把主线程放在子线程之前了

  • 这样主线程一直是先跑完的,相当于是一个单线程的效果

方式二:实现Runnablel接口

多线程的实现方案二:实现Runnable接口

  • 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
  • 创建MyRunnable任务对象
  • 把MyRunnable任务对象交给Thread处理
  • 调用线程对象的start()方法启用线程
Thread的构造器
构造器说明
public Thread(String name)可以为当前线程指定名称
public Thread(Runnable target)封装Runnable对象成为线程对象
public Thread(Runnable target , String name)封装Runnable对象成为线程对象并指定线程名称

 2.第二种方式的优缺点

  • 优点:线程任务类只是实现了Runnable接口,可以继续继承和实现
  • 缺点:如果线程有执行结果是不能返回的

方式三:JDK5.0新增Callable接口

  1. 多线程的实现方法三:利用Callable、Futuretask接口实现
  2. 得到任务对象
  3. 定义类实现Callable接口,重写call方法,封装要做的事情
  4. 用FutureTask把Callable对象封装成线程任务对象
  5. 把线程任务对象交给Thread处理
  6. 调用Thread的start方法启动线程,执行任务
  7. 线程执行完毕后,通过FutureTask的get方法去获取任务执行的处理
FutureTask的API
方法名称说明
public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象。
public V get()throws Exception获取线程执行call方法返回的结果

方式三的优缺点:

  • 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
  • 可以子啊线程执行完毕之后去获取线程执行结果。
  • 缺点:编码稍微复杂

Thread的常用方法

1.当有很多线程在执行的时候,我们应该怎么去区分这些线程呢?

此时需要使用Thread的常用Thread的方法:getName()、setName()、currentThread()deng

Thread的构造器
方法名称说明
public Thread(String name)可以为当前线程指定名称
public Thread(Runnable target)封装Runnable对象成为线程对象
public Thread(Runnable target , String name)封装成Runnable对象成为线程对象,并指定线程名称
Thread的线程休眠方法
方法名称说明
public static void sleep(long time)让当前线程休眠指定时间后再继续执行,单位为毫秒
Thread的常用方法和构造器
方法名称说明
public static void sleep(long time)让当前线程休眠指定时间后再继续执行,单位为毫秒
public void start()线程启动方法
public void run()线程任务方法
public static Thread currentThread();返回对当前正在执行的线程对象的引用
String getName()

获取当前线程的名称,默认线程名称是Thread-索引

void setName(String name)设置线程名称

线程安全

线程安全问题

  • 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。

线程安全问题出现的原因?

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改共享资源

线程同步

如何才能保证线程安全?

  • 让多个线程实现先后依次访问共享资源,这样就解决了安全问题

线程同步的核心思想

  • 加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来

 同步代码块

  • 作用:把出现线程安全问题的核心代码给上锁。
  • 原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。
synchronized(同步锁对象){操作共享资源的代码(核心代码)
}

锁对象要求

  • 理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可

锁对象用任意唯一的对象好不好?

  • 不好,会影响其他无关线程的执行

锁对象的规范要求

  • 规范上:建议使用共享资源作为锁对象
  • 对于实例方法建议使用this作为锁对象
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象

 同步代码块是如何实现线程安全的

  • 对于出现问题的核心代码使用synchronized进行加锁

每次只能一个线程占锁进入访问

  • 同步代码块的同步锁对象有什么要求
  • 对于实例方法建议使用this作为锁对象
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象

 同步方法

  • 作用:把出现线程安全问题的核心方法上锁
  • 原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行
修饰符 synchronized 返回值类型 方法名称(形参列表){操作共享资源代码
}

 同步方法底层原理

  • 同步方法底层也是隐式锁对象,只是锁的范围是整个方法代码。
  • 如果方法是实例化,同步方法默认用this作为锁对象。但是代码要高度面向对象。
  • 如果方法使用的是静态方法,同步方法默认用类名.class作为的锁对象。

 Lock锁

  • 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便
  • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
  • Lock是接口不能直接实例化,这里采用,他的实现类ReentrantLock来构建Lock锁对象

方法名称说明
public ReentrantLock()获得Lock锁的实现类对象

Lock的API
方法名称说明
void lock()获得锁
void unlock()释放锁

线程通信

什么是线程通信?

  • 线程通信是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现
  • 线程间会根据共享数据的情况来决定自己该做什么,以及通知其他线程该怎么做

线程通信常见模型

  • 生产者与消费者模型:生产者线程负责圣生产数据,消费者线程负责消费数据
  • 要求:生产者线程生产完数据后,唤醒消费者,然后等待自己;消费者消费完数据后,唤醒生产者,然后等待自己。

线程通信的前提:线程通信通常是在多个线程操作同一个共享资源的时候需要进行的通信,且要保证线程安全。

线程通信的三个常见方法

方法名称说明
void wait()当前线程等待,直到另一个线程调用notify()或notifiAll()唤醒自己
void notify()唤醒正在等待对象监视器(锁监视对象)的单个线程
void notifyAll()唤醒正在等待对象检测器(锁对象)的所有进程

注意:上述方法应该使用当前同步锁(this)对象进行调用

线程池【重点】

什么是线程池?

  • 线程池就是一个可以服用线程技术。

不使用线程池的问题

  • 如果用户每发起一个请求,然后后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而新线程的开销是很大的,这样会严重影响系统的性能。

谁代表线程池?

JDK5.0起提供了代表线程池的接口:Executor Service

如何得到线程池对象

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象 

 

线程池常见面试题

临时线程什么时候创建?

  • 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

什么时候会开始拒绝任务?

  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来才会开始任务拒绝。

谁代表线程池?

ExecutorService接口 :已知实现类:

AbstractExecutorServiceForkJoinPool 

ScheduledThreadPoolExecutorThreadPoolExecutor(重点)

 ThreadPoolExecutor创建线程池对象示例

//创建线程池对象
ExecutorService pool =new ThreadPoolExecutor(3(核心线程),5(最大线程数), 6(最大存活时间),TimeUnit.SECONDS(指定存活时间单位), new ArrayBlockingQueue<>(5)(指定任务队列),Executors.defaultThreadFactory()(指定线程工厂创建线程),new ThreadPoolExecutor.AbortPolicy()(新任务来了处理办法));
ExecutorService的常用方法
方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行Runnable任务
Future<T> submit(Callable<T> task)执行任务,返回未来任务对象获取线程结果,一班拿来执行Callable任务
void shutdown()等任务执行完毕后关闭线程
List<Runnable> shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
新任务拒绝策略
策略详解
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionExeceotion异常。是默认策略
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常,这是不推荐的做法
ThreadPoolExecutor.DiscardoldesPolicy抛出队列中等待最久的任务 然后把房钱队列加入队列中
ThreadPoolExecutor.CallerRunnablePolicy由主线程负责调用任务的run()方法从而绕过线程池直接执行

线程池如何处理Runnable任务

  • 使用ExecutorService的方法
  • void execute(Runnable target)

线程池处理Callable任务

ExecutorService的常用方法
方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般执行Ruannable任务
Future<T> submit(Callable<T> task)执行Callsble任务,返回未来任务对象获得线程结果
void shutdown()等任务执行完毕后关闭线程池
List<Runnable> shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务

 线程池如何处理Callable任务,并得到任务执行完毕后返回的结果

  • 使用ExecutorService的方法:
  • Future<T> submit(Callable<t> command)

Executors工具类实现线程池 

 Executors工具类底层是基于什么方式实现的线程池对象?

  • 线程池ExecutorService的实现类:ThreadPoolExecutor

Executor是否适合做大型互联网场景的线程池方案?

  • 不合适
  • 建议使用THreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险

定时器

  • 定时器是一种控制任务延时调用,或者周期调用的技术
  • 作用:闹钟、定时邮件发送

定时器的实现方式

  • 方式一:Timer
  • 方式二:ScheduledExecutorService
Timer定时器
构造器说明
public Timer()创建Timer定时器对象
方法说明
public void schedule(TimewTask, long delay ,long period)开启一个定时器,按照计划处理TimerTask任务

Timer定时器的特点和存在的问题

  1. Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入
  2. 可能会因为其中其中某个任务的异常使Timer线程死掉,从而影响后续任务的执行。

ScheduledExecutorService定时器

ScheduledExecutorService是JDK1.5中引入了并发包,目的是为了弥补TImer的缺陷,ScheduledExecutorService内部为线程池。

Executors的方法说明
public ScheduledExecutorService newScheduledThreadPool(int corePoolSize)得到线程池对象

 

ScheduledExecutorService的方法说明
public ScheduledFuture<?> scheduleAtFixedRate(Runable command, long initaDelay, long period, TimeUi)周期调度方法

ScheduledExecutorService的优点

  1. 基于线程池,某个任务的执行情况不会影响其他定时任务的执行。

 

补充知识:并发、并行

并发与并行

  • 正在执行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实就是并发与并行同时进行的。

并发的理解:

  • CPU同时处理线程的数量有限。
  • CPU会轮询为系统每个线程服务,由于CPU切换的速度很快,给我们的感觉是这些线程是同时执行的,这就是并发。

并行的理解:

  • 在同一时刻上,同时有很多个线程在被CPU处理并执行。

并行与并发的含义:

  • 并发:CPU分时轮询的执行线程
  • 并行:同一时刻同时在执行

生命周期

线程的状态

  • 线程的状态:就是线程从生到死的过程,记忆中间经历的各种非状态及状态转换。
  • 理解线程的状态有利提升并发编程的理解能力。

Java线程的状态

  • Java总共定义了6种状态
  • 6种状态都定义在Thread的内部枚举类种。

  

线程的六种状态:

新建状态(NEW)------------------->   创建线程对象

就绪状态(RUNNABLE)------------------->   start方法

阻塞状态(BLOCKED)------------------->   无法获得锁对象

等待状态(WAITING)------------------->   wait方法

计时等待(TIME_WAITING)------------------->   sleep方法

结束状态(TERMINATED)------------------->   全部代码运行完毕


http://chatgpt.dhexx.cn/article/HDuPGqEs.shtml

相关文章

什么是单线程和多线程,单线程与多线程的区别

什么是进程&#xff1f; 当一个程序开始运行时&#xff0c;它就是一个进程&#xff0c;进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。 什么是线程&#xff1f; 线程是程序中的一个执行流&#xff0c;每个线程都有自己的专有寄存器…

理解单线程和多线程的区别

单线程和多线程 首先我们都知道线程是隶属于进程的,单线程就是程序中只有一个线程,多线程就是程序中有多个线程.通过连个例子理解多线程和单线程. (一)单线程 创建包:package com.ffyc.javathread; 创建类:public class Sample { } package com.ffyc.javathread; public c…

ubuntu卸载已安装软件

1.查看已安装软件列表 dpkg -l dpkg -l | grep baidu # 软件包查询筛选# 另外&#xff0c;软件安装也使用dpkg命令 sudo dpkg -i xxxx.deb2.卸载指定软件包 sudo apt-get purge baidunetdisk # 卸载指定软件 sudo apt-get autoremove # 清理不需要的文件 sudo apt-get autoc…

Ubuntu20.04 卸载软件

Ubuntu20.04卸载软件相当的简单&#xff0c; 一 用命令行卸载 sudo apt-get remove --purge 软件名 这样不仅可以卸载掉软件&#xff0c;而且可以清理配置 二 在图形界面卸载软件 首先打开设置 打开应用程序 在其中找到你要卸载的软件 然后点击右上角的 在软件中打开 然后…

Linux bash卸载软件,Ubuntu卸载软件的4种方法

前言 本文重点介绍Ubuntu卸载软件的4种方法。他们分别是图形化界面的synaptic、自动解决依赖关系的apt-get,处理依赖关系更强大的aptitude,还有安装本地deb包的dpkg。 方法一: Ubuntu使用synaptic图形化界面管理软件 oucanrong@zcwyou:~$ sudo apt-get install synaptic -y 以…

Ubuntu卸载软件方式

1、终端输入&#xff1a; dpkg --list 显示出电脑上所有的软件包 左边就是软件名字&#xff0c;中间是版本名 2、在终端上输入命令sudo apt --purge remove 包名 例如我要卸载smartgit 即可

Ubuntu卸载软件

1、利用"软件中心"卸载 在Ubuntu收藏栏中打开软件中心&#xff0c;在已安装的软件中找到要卸载的软件&#xff0c;点击“移除”按钮即可。 提示&#xff1a;需要输入用户密码。 2、在终端中卸载 2.1、找到软件 有些软件在“软件中心”中是找不到的。还是需要用终端…

Ubuntu卸载软件相关命令

查看安装的软件 dpkg -l 查看软件版本 dpkg -l | grep xxx # xxx为软件名称&#xff0c;例如 virtualbox 查看包是否安装 dpkg-query -l *package-name* 例如 virtualbox dpkg -s package-name 例如 virtualbox 列出你系统中安装的所有包&#xff0c;同样可以通过grep…

Ubuntu命令卸载软件

1.打开一个终端&#xff0c;输入dpkg --list ,按下Enter键&#xff0c;终端输出以下内容&#xff0c;显示的是你电脑上安装的所有软件。 2.在终端中找到你需要卸载的软件的名称&#xff0c;列表是按照首字母排序的。 3.在终端上输入命令sudo apt-get --purge remove 包名…

A星算法优化(二)权重系数

本文接上一篇&#xff1a;A星算法优化&#xff08;一&#xff09;启发函数 B站详解视频&#xff1a;https://www.bilibili.com/video/BV1EF411W7Rw?spm_id_from333.999.0.0 将从以下5个点进行改进&#xff1a; 1、启发函数——曼哈顿距离等 2、权重系数——动态加权等 3、搜索…

A_star算法

A*算法 A* 搜索算法&#xff0c;即A star search algorithm&#xff0c;简称A* 算法。 是一种在图形平面上对于多个节点的路径求出最低通过成本的算法。是对图的遍历和最佳优先搜索算法&#xff0c;也是对BFS的改进。其思想在于为每个状态建立启发函数&#xff0c;用启发函数制…

A star算法

A star算法介绍 我们在解空间搜索问题的可行解或者最优解时常用宽度优先搜索(BFS)或者深度优先搜索(DFS)&#xff0c;但是有时候会扩展出很多无用节点&#xff0c;搜索时间较长&#xff0c;而A*算法则是选择当前估计成本最低的节点进行扩展&#xff0c;图示如下: g(n)为从起始…

【A星算法】A星寻路算法详解(小白也可以看懂+C#代码+零基础学习A*)

1.问题背景 在制作RPG游戏角色和NPC移动时&#xff0c;需要角色自动避开障碍物&#xff0c;到达终点 怎么快速找到一条到达终点的路径&#xff1f; 使用a星寻路算法 2.A星算法的思路 绿色&#xff1a;起点&#xff1b;红色&#xff1a;终点 &#xff1b;黑色&#xff1a;障碍…

A-star算法自学

搜索过程 广度优先搜索&#xff08;BFS&#xff09;算法与Dijsktra算法的结合&#xff0c;可以得出最短的路径。 将地图信息通过划分为方形或者其他多边形格子的方法进行表示&#xff0c;便于利用二维数组存放地图信息&#xff0c;每个格子有可行和不可行两种状态&#xff1b;…

python3.6实现的A星算法

A星算法原理: 原理我就不再赘述,可以参考这篇博客https://blog.csdn.net/hitwhylz/article/details/23089415 最近用js写了一遍&#xff0c;用的同样的算法&#xff0c;需要js代码的看这里&#xff1a;https://blog.csdn.net/qq_39687901/article/details/85697127 代码实现: …

A星寻路算法介绍

你是否在做一款游戏的时候想创造一些怪兽或者游戏主角&#xff0c;让它们移动到特定的位置&#xff0c;避开墙壁和障碍物呢&#xff1f; 如果是的话&#xff0c;请看这篇教程&#xff0c;我们会展示如何使用A星寻路算法来实现它&#xff01; 在网上已经有很多篇关于A星寻路算…

对A星算法的理解

1、A*算法的**搜索区域 ** 传统A星算法是将地图简化成栅格&#xff0c;计算路径时是用每个栅格的中心点作为单位进行计算。 搜索区域分为两部分&#xff1a;开放列表和封闭列表 开放列表可以进行访问&#xff0c;封闭列表则不可以访问&#xff08;包括不可走 (unwalkable) 的方…

A星算法(Java实现)

一、适用场景 在一张地图中&#xff0c;绘制从起点移动到终点的最优路径&#xff0c;地图中会有障碍物&#xff0c;必须绕开障碍物。 二、算法思路 1. 回溯法得到路径 (如果有路径)采用“结点与结点的父节点”的关系从最终结点回溯到起点&#xff0c;得到路径。 2. 路径代…

A-Star(A*)算法

【由于专栏后几篇限制vip观看&#xff0c;我又把完整算法在公众号“武汉AI算法研习”进行了发布&#xff0c;可以查看全专栏文章。】 A-Star(A*)算法作为Dijkstra算法的扩展&#xff0c;在寻路和图的遍历过程中具有一定的高效性。 【适用范围】静态图搜索 【算法流程】A-Sta…

A星算法代码

A星算法代码python3实现 前言一、A*&#xff1f; A星1.一个搜索算法2.结果展示 二、使用环境1.python 3.x2.一些解释说明 一些话 前言 产生本文的缘由 学校计科课程要求的小作业, 在csdn上看了好多, 记录一下自己的学习 以下是本篇文章正文内容 一、A*&#xff1f; A星 1.一…