秋招Java后端开发冲刺——并发篇1(线程与进程、多线程)

一、进程

1. 进程
进程是程序的一次动态执行过程,是操作系统资源分配的基本单位。
2. 进程和线程的区别

特性进程线程
定义独立运行的程序实例,资源分配的基本单位进程中的一个执行单元,CPU调度的基本单位
资源进程拥有独立的内存空间和资源线程共享进程的堆和方法区(JDK1.8之后为元空间)
通信进程间通信(IPC)较为复杂线程间通信(共享内存)较为简单
开销创建和销毁进程开销较大创建和销毁线程开销较小
独立性进程之间相对独立线程间相互影响
并发性进程可以并发执行线程可以并发执行
调度由操作系统调度由操作系统或线程库调度
崩溃影响一个进程崩溃不会影响其他进程一个线程崩溃可能会影响整个进程

3. 进程和线程的联系

联系描述
组成关系一个进程可以包含一个或多个线程,线程是进程的一部分,多个线程共享进程的资源。
资源共享线程共享进程的堆和方法区(JDK1.8之后为元空间)、文件句柄等资源,进程则有自己的独立资源。
并发执行进程和线程都可以并发执行,利用多核 CPU 提高程序的并行度。
调度进程和线程都由操作系统进行调度,多线程程序中,线程的调度可以由 JVM 和操作系统共同管理。

二、线程

1. Java线程

  • JDK 1.2 之前,Java 线程是基于绿色线程(Green Threads)实现的,这是一种用户级线程(用户线程);JDK1.2之后Java线程是基于原生线程(Native Threads,操作系统内核线程)实现。
  • 虚拟机栈和本地方法栈线程私有是为了保证局部变量不被其他线程访问
  • 程序技术器线程私有是为了线程切换后能找到上次运行的位置继续执行

2. 线程的创建方式

  • 继承Thread类并重写 run 方法来定义线程的执行逻辑。
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running");}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();  // 启动线程}
}
  • 实现Runable接口并将其实例传递给 Thread 对象
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Runnable is running");}public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();  // 启动线程}
}
  • 实现Callable接口并使用 FutureTask 包装 Callable 对象,然后将其传递给 Thread 对象(Callable 可以有返回值,且可以抛出异常
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "Callable is running";}public static void main(String[] args) {MyCallable callable = new MyCallable();FutureTask<String> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();  // 启动线程try {// 获取执行结果String result = futureTask.get();System.out.println(result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}
  • 其他方式:使用线程池、使用CompletableFuture类

3. 线程(Thread类)的常见方法

方法名描述
void start()启动线程,调用线程的 run 方法
void run()线程的执行方法,需要重写
void interrupt()中断线程
boolean isInterrupted()测试线程是否已经中断
static boolean interrupted()测试当前线程是否已经中断,并清除当前线程的中断状态
void join()等待线程终止
void join(long millis)等待线程终止最长时间为 millis 毫秒
void join(long millis, int nanos)等待线程终止最长时间为 millis 毫秒加 nanos 纳秒
static void sleep(long millis)使当前线程睡眠(暂停执行)指定的毫秒数
static void sleep(long millis, int nanos)使当前线程睡眠(暂停执行)指定的毫秒数加纳秒数
void setPriority(int newPriority)更改线程的优先级
int getPriority()返回线程的优先级
void setName(String name)更改线程名称
String getName()返回线程名称
long getId()返回线程的唯一标识符
Thread.State getState()返回线程的状态
boolean isAlive()测试线程是否还活着
static void yield()暂停当前正在执行的线程对象,并执行其他线程
static Thread currentThread()返回对当前正在执行的线程对象的引用
static int activeCount()返回当前线程的线程组中活动线程的数目
static void dumpStack()将当前线程的堆栈跟踪打印到标准错误流
StackTraceElement[] getStackTrace()返回一个数组,表示该线程的堆栈转储
static boolean holdsLock(Object obj)当且仅当当前线程在指定的对象上保持监视器锁时,返回 true
void setDaemon(boolean on)将该线程标记为守护线程或用户线程
boolean isDaemon()测试该线程是否为守护线程
void checkAccess()判断当前运行的线程是否有权限修改此线程
ThreadGroup getThreadGroup()返回该线程所属的线程组

4. 线程的生命周期

  • New (新建状态):线程对象被创建,但还未调用 start() 方法。
  • Runnable (就绪状态):start() 方法被调用,线程进入就绪状态,等待 CPU 时间片的分配。
  • Running (运行状态):线程获得 CPU 时间片,开始执行 run() 方法中的代码。
  • Blocked (阻塞状态):线程因等待资源或锁而进入阻塞状态,无法继续执行。
  • Waiting (等待状态):线程等待另一个线程显式地唤醒自己,通过 wait()、join() 或 sleep() 等方法进入等待状态。
  • Timed Waiting (计时等待状态):线程等待一定时间后会被自动唤醒,通过 sleep(long millis)、wait(long timeout) 或 join(long millis) 等方法进入计时等待状态。
  • Terminated (终止状态):线程运行结束或因异常退出 run() 方法,线程进入终止状态
    以下是线程生命周期的图解:
    (1)JDK1.5之前

    (2)JDK1.5之后
    在这里插入图片描述

三、多线程

1. 线程安全问题的解决方式

(1)产生原因

  • 共享资源:多个线程同时访问和修改同一资源,例如变量、对象、文件等。
  • 缺乏同步:线程在访问共享资源时,没有正确使用同步机制,导致多个线程同时执行对共享资源的操作。
  • 原子性操作的缺乏:对共享资源的操作需要分多个步骤完成,如果这些步骤不能保证原子性,会导致线程安全问题。
  • 可见性问题:一个线程对共享资源的修改,其他线程不能立即看到,导致数据不一致。
  • 指令重排序:编译器和处理器为了优化性能,可能会对指令进行重排序,导致线程安全问题

(2)解决方式

  • Synchronized关键字:同步块可以确保在同一时间只有一个线程执行同步代码,从而避免多个线程同时访问共享资源的问题。(详解请参考)
public class SynchronizedExample {public synchronized void synchronizedMethod() {// 同步实例方法// 其他线程不能同时执行此方法}public static synchronized void staticSynchronizedMethod() {// 同步静态方法// 其他线程不能同时执行此静态方法}public void synchronizedBlock() {synchronized (this) {// 同步代码块// 锁定当前实例对象}}
}
  • Lock锁:是 Java 提供的一种显式锁机制,有多种锁类型实现。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private final Lock lock = new ReentrantLock();public void lockMethod() {lock.lock();try {// 临界区代码// 其他线程不能同时执行此代码} finally {lock.unlock();}}
}
  • 使用线程本地变量 (ThreadLocal):每个线程都有自己的变量副本,互不干扰。
public class ThreadLocalExample {private ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);public void increment() {threadLocal.set(threadLocal.get() + 1);}public int get() {return threadLocal.get();}
}
  • Atomic Variables(原子变量):java.util.concurrent.atomic 包提供了多种原子变量,如 AtomicInteger、AtomicLong、AtomicReference 等,它们提供了一种无锁的线程安全机制。
import java.util.concurrent.atomic.AtomicInteger;public class AtomicExample {private final AtomicInteger counter = new AtomicInteger(0);public void increment() {counter.incrementAndGet();}public int getCounter() {return counter.get();}
}

(3) Synchronized 和 Lock 的区别

特性synchronizedLock
实现内置语言特性通过 java.util.concurrent.locks 包提供
锁的释放自动释放:线程退出同步代码块或方法时自动释放需要显式调用 unlock() 方法
灵活性灵活性较低,只能锁定方法或代码块灵活性较高,可以尝试获取锁、定时获取锁等
锁的获取线程阻塞式等待支持阻塞式、非阻塞式、定时尝试获取锁
性能较低:适用于简单的同步较高:适用于复杂的并发控制
条件变量提供 Condition 类,支持多个条件变量
中断响应不支持线程中断支持线程中断,响应中断请求
读写锁不支持支持,通过 ReentrantReadWriteLock 实现
公平锁不支持支持公平锁,通过 ReentrantLock 实现

2. 死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

(1)死锁的产生:四个必要条件

  • 非抢占式:线程已获得的资源在未使用完之前不能被其他线程强行剥夺
  • 循环等待:若干线程之间形成一种头尾相接的循环等待资源关系
  • 互斥条件:该资源任意一个时刻只由一个线程占用
  • 请求与保持条件:个线程因请求资源而阻塞时,对已获得的资源保持不放

(2)死锁的预防:破坏必要条件

  • 破坏请求与保持条件:一次性申请所有的资源(会造成内存开销极大,因为程序可能很长一段时间使用不到该资源)。
  • 破坏非抢占式条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件:按某一顺序申请资源,释放资源则反序释放。

(3)死锁的避免

  • 在资源分配时,借助于算法(比如银行家算法)对资源分配进行计算评估,使其进入安全状态。
  • 安全状态:系统能够按照某种线程推进顺序(P1、P2、P3……Pn)来为每个线程分配所需资源,直到满足每个线程对资源的最大需求,使每个线程都可顺利完成。
  • 银行家算法代码实现
import java.util.Arrays;public class BankersAlgorithm {private int numProcesses;private int numResources;private int[] available;private int[][] maximum;private int[][] allocation;private int[][] need;public BankersAlgorithm(int numProcesses, int numResources) {this.numProcesses = numProcesses;this.numResources = numResources;this.available = new int[numResources];this.maximum = new int[numProcesses][numResources];this.allocation = new int[numProcesses][numResources];this.need = new int[numProcesses][numResources];}public void setAvailable(int[] available) {System.arraycopy(available, 0, this.available, 0, numResources);}public void setMaximum(int process, int[] max) {System.arraycopy(max, 0, this.maximum[process], 0, numResources);for (int j = 0; j < numResources; j++) {this.need[process][j] = this.maximum[process][j] - this.allocation[process][j];}}public void setAllocation(int process, int[] alloc) {System.arraycopy(alloc, 0, this.allocation[process], 0, numResources);for (int j = 0; j < numResources; j++) {this.need[process][j] = this.maximum[process][j] - this.allocation[process][j];}}public boolean requestResources(int process, int[] request) {// Step 1: Check if request <= needfor (int j = 0; j < numResources; j++) {if (request[j] > need[process][j]) {return false; // Request exceeds need}}// Step 2: Check if request <= availablefor (int j = 0; j < numResources; j++) {if (request[j] > available[j]) {return false; // Request exceeds available resources}}// Step 3: Pretend to allocate requested resourcesfor (int j = 0; j < numResources; j++) {available[j] -= request[j];allocation[process][j] += request[j];need[process][j] -= request[j];}// Step 4: Check system safetyif (checkSafety()) {return true; // Safe state, allocation is successful} else {// Revert allocation if not safefor (int j = 0; j < numResources; j++) {available[j] += request[j];allocation[process][j] -= request[j];need[process][j] += request[j];}return false; // Not a safe state}}private boolean checkSafety() {boolean[] finish = new boolean[numProcesses];int[] work = Arrays.copyOf(available, numResources);while (true) {boolean foundProcess = false;for (int i = 0; i < numProcesses; i++) {if (!finish[i]) {boolean canAllocate = true;for (int j = 0; j < numResources; j++) {if (need[i][j] > work[j]) {canAllocate = false;break;}}if (canAllocate) {for (int j = 0; j < numResources; j++) {work[j] += allocation[i][j];}finish[i] = true;foundProcess = true;}}}if (!foundProcess) {break;}}for (boolean f : finish) {if (!f) {return false; // System is not in a safe state}}return true; // System is in a safe state}public static void main(String[] args) {int numProcesses = 5;int numResources = 3;BankersAlgorithm ba = new BankersAlgorithm(numProcesses, numResources);ba.setAvailable(new int[]{10, 5, 7});ba.setMaximum(0, new int[]{7, 5, 3});ba.setMaximum(1, new int[]{3, 2, 2});ba.setMaximum(2, new int[]{9, 0, 2});ba.setMaximum(3, new int[]{2, 2, 2});ba.setMaximum(4, new int[]{4, 3, 3});ba.setAllocation(0, new int[]{0, 1, 0});ba.setAllocation(1, new int[]{2, 0, 0});ba.setAllocation(2, new int[]{3, 0, 2});ba.setAllocation(3, new int[]{2, 1, 1});ba.setAllocation(4, new int[]{0, 0, 2});int[] request = {1, 0, 2};int process = 1;boolean success = ba.requestResources(process, request);System.out.println("Request " + (success ? "granted" : "denied"));}
}

3. 线程池
(1)核心参数

  • 核心线程数 (corePoolSize):线程池中保持活动的最小线程数量,即使这些线程处于空闲状态。
  • 最大线程数 (maximumPoolSize):线程池中允许的最大线程数量。当任务队列已满且已达到核心线程数时,线程池会创建新的线程来处理任务,直到达到最大线程数。达到最大线程数后,新任务将被拒绝处理,并根据饱和策略进行处理。
  • 空闲线程存活时间 (keepAliveTime):当线程池中线程数量超过核心线程数时,多余的空闲线程在等待新任务时的最长存活时间。超过这个时间的空闲线程将被终止和移除,直到线程池中的线程数量等于核心线程数。
  • 时间单位 (unit):空闲线程存活时间的单位,如秒、毫秒等。与 keepAliveTime 参数一起使用。
  • 任务队列 (workQueue):用于保存等待执行任务的阻塞队列。常用的队列实现有:
    • 直接提交队列 (SynchronousQueue):不保存任务,每个插入操作必须等待相应的删除操作。
    • 有界队列 (ArrayBlockingQueue):有固定容量的队列,当队列满时,插入操作将被阻塞。
    • 无界队列 (LinkedBlockingQueue):队列大小没有上限,理论上可以无限制地增加队列长度。
    • 优先队列 (PriorityBlockingQueue):按任务优先级排序的无界队列。
  • 线程工厂 (threadFactory):用于创建新线程的工厂。通过自定义线程工厂,可以为每个新线程设置名称、优先级等属性。
  • 拒绝策略 (handler):当任务队列已满且线程池中的线程数量已达到最大线程数时,如何处理新任务。Java 提供了四种预定义的拒绝策略:
    • AbortPolicy(默认):抛出 -
    • RejectedExecutionException,拒绝任务。
    • CallerRunsPolicy:由调用线程处理该任务。
    • DiscardPolicy:丢弃无法处理的任务,不予处理。
    • DiscardOldestPolicy:丢弃最早添加到队列中的任务,然后尝试重新提交新任务。

(2)线程池创建

  • 固定大小的线程池 (Fixed Thread Pool)
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
  • 单线程化的线程池 (Single Thread Executor)
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  • 缓存的线程池 (Cached Thread Pool)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  • 定时任务线程池 (Scheduled Thread Pool)
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);

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

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

相关文章

磁力泵与屏蔽泵

1.磁力泵的工作原理 磁力传动是利用磁体能吸引铁磁物质以及磁体或磁场之间有磁力作用的特性&#xff0c;而非铁磁物质不影响或很少影响磁力的大小&#xff0c;因此可以无接触地透过非磁导体&#xff08;隔离套&#xff09;进行动力传输。磁力传动可分为同步或异步设计。 大多数…

苹果电脑清理app垃圾高效清理,无需专业知识

在我们的日常使用中&#xff0c;苹果电脑以其优雅的设计和强大的功能赢得了广泛的喜爱。然而&#xff0c;即便是最高效的设备&#xff0c;也无法免俗地积累各种不必要的文件和垃圾&#xff0c;特别是app垃圾。所以&#xff0c;苹果电脑清理app垃圾高效清理&#xff0c;对于大多…

Spring Boot集成olingo快速入门demo

1.什么是olingo&#xff1f; Apache Olingo 是个 Java 库&#xff0c;用来实现 Open Data Protocol (OData)。 Apache Olingo 包括服务客户端和 OData 服务器方面。 Open Data Protocol &#xff08;开放数据协议&#xff0c;OData&#xff09; 是用来查询和更新数据的一种W…

单调栈 求下一个更大数

题意&#xff1a; 现在给你n个数字: ,问从每个数字往后看&#xff0c;第一个比他大的数字的下标是多少。 题解&#xff1a; 使用一个单调递减栈即可。 #include<bits/stdc.h> using namespace std; const int N100005;int n,s[N],a[N],ans[N],top0;int main(){scan…

ASP.NET Web应用中的 Razor Pages/MVC/Web API/Blazor

如果希望使用ASP.NET Core创建新的 Web 应用程序&#xff0c;应该选择哪种方法&#xff1f;Razor Pages还是 MVC&#xff08;模型-视图-控制器&#xff09;&#xff0c;又或者使用Web API Vue/React/......。 每种方法都有各自的优点和缺点。 什么是 MVC&#xff1f; 大多数服…

高考志愿填报,选热门专业还是选自己喜欢的专业

对于每一个结束高考的学生来说&#xff0c;都要面临选专业这个严峻的挑战。选专业可以说是妥妥的大工程&#xff0c;因为这关系到接下来的几年要学什么内容&#xff0c;关键是未来的几十年要从事什么样的工作。 所以在谈及选专业这个问题的时候&#xff0c;每个人的内心都有些…

力扣(3200)- 三角形的最大高度

好方法&#xff1a; 垃圾方法&#xff1a;

Python酷库之旅-第三方库Pandas(005)

目录 一、用法精讲 7、pandas.read_clipboard函数 7-1、语法 7-2、参数 7-3、功能 7-4、返回值 7-5、说明 7-6、用法 7-6-1、代码示例 7-6-2、结果输出 8、pandas.DataFrame.to_clipboard函数 8-1、语法 8-2、参数 8-3、功能 8-4、返回值 8-5、说明 8-6、用法…

UCOS-III 任务同步机制-信号量

1. 信号量类型 1.1 二值信号量&#xff08;Binary Semaphores&#xff09; 二值信号量只有两个状态&#xff1a;可用&#xff08;1&#xff09;和不可用&#xff08;0&#xff09;。它主要用于任务之间的互斥访问或者事件通知。例如&#xff0c;当一个任务完成某个操作后&am…

pip install包出现哈希错误解决

如图&#xff0c;当遇到此类错误时&#xff0c;多半是连接不稳定导致的校验失败。我们可以在PC端&#xff0c;或Ubuntu通过浏览器下载.whl安装文件&#xff1a;直接复制报错信息中的网址到浏览器即可弹出下载窗口。

kafka的架构

一、架构图 Broker&#xff1a;一台 kafka 服务器就是一个 broker。一个kakfa集群由多个 broker 组成。一个 broker 可以容纳多个 topic。 Producer&#xff1a;消息生产者&#xff0c;就是向 kafka broker 发消息的客户端 Consumer&#xff1a;消息消费者&#xff0c;向 kaf…

Win11右键默认显示更多选项的方法

问题描述 win11系统默认右键菜单显示选项太少&#xff0c;每次需要点一下“显示更多选项”才能得到想要内容。比方说我用notepad打开一个文档&#xff0c;在win11上要先点一下"显示更多选项“&#xff0c;再选择用notepad打开&#xff0c;操作非常反人类。 Win11右键默…

小红书矩阵系统源码:赋能内容创作与电商营销的创新工具

在内容驱动的电商时代&#xff0c;小红书凭借其独特的社区氛围和用户基础&#xff0c;成为品牌营销和个人创作者不可忽视的平台。小红书矩阵系统源码&#xff0c;作为支撑这一平台的核心技术&#xff0c;提供了一系列的功能和优势&#xff0c;助力用户在小红书生态中实现更高效…

高考假期预习指南

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

【CSAPP】-attacklab实验

目录 实验目的与要求 实验原理与内容 实验设备与软件环境 实验过程与结果&#xff08;可贴图&#xff09; 实验总结 实验目的与要求 1. 强化机器级表示、汇编语言、调试器和逆向工程等方面基础知识&#xff0c;并结合栈帧工作原理实现简单的栈溢出攻击&#xff0c;掌握其基…

51单片机第23步_定时器1工作在模式0(13位定时器)

重点学习51单片机定时器1工作在模式0的应用。 在51单片机中&#xff0c;定时器1工作在模式0&#xff0c;它和定时器0一样&#xff0c;TL1占低5位&#xff0c;TH1占高8位&#xff0c;合计13位&#xff0c;也是向上计数。 1、定时器1工作在模式0 1)、定时器1工作在模式0的框图…

django高校教务系统-计算机毕业设计源码81661

目 录 摘要 1 绪论 1.1 研究背景 1.2目的及意义 1.3论文结构与章节安排 2 高校教务系统设计分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4…

TCP和IP数据包结构

一、问题引入 一般我们在谈上网速度的时候&#xff0c;专业上用带宽来描述&#xff0c;其实无论说网速或者带宽都是不准确的&#xff0c;呵呵。比如&#xff1a;1兆&#xff0c;512K……有些在学校的学生&#xff0c;也许会有疑问&#xff0c;明明我的业务是1M&#xff0c;为…

前后端的导入、导出、模板下载等写法

导入&#xff0c;导出、模板下载等的前后端写法 文章目录 导入&#xff0c;导出、模板下载等的前后端写法一、导入实现1.1 后端的导入1.2 前端的导入 二、基础的模板下载2.1 后端的模板下载-若依基础版本2.2 前端的模板下载2.3 后端的模板下载 - 基于资源文件读取2.4 excel制作…

Tech Talk:智能电视eMMC存储的五问五答

智能电视作为搭载操作系统的综合影音载体&#xff0c;以稳步扩大的市场规模走入越来越多的家庭&#xff0c;成为人们生活娱乐的重要组成部分。存储部件是智能电视不可或缺的组成部分&#xff0c;用于保存操作系统、应用程序、多媒体文件和用户数据等信息。智能电视使用eMMC作为…