多线程(Lock锁,死锁,等待唤醒机制,阻塞队列,线程池)

Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
void lock():获得锁void unlock():释放锁
即手动上锁、手动释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例


例子:

package Threadmethod;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class MyThread1 extends Thread{static int jicket=0;static Lock lock=new ReentrantLock();@Overridepublic void run() {while (true){lock.lock();try {if(jicket==100){break;}else {Thread.sleep(10);jicket++;System.out.println(getName()+ "正在卖第"+jicket+"张票");}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}

测试类 

package Threadmethod;public class ThreadDemo8 {public static void main(String[] args) {MyThread1 t1=new MyThread1();MyThread1 t2=new MyThread1();MyThread1 t3=new MyThread1();t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

死锁

死锁是指在多个进程或线程中,每个进程或线程因为等待另一个进程或线程所拥有的资源而进入无限等待的状态,导致系统无法继续执行下去的情况。死锁通常发生在以下情况下:

1. 互斥:多个进程或线程同时只能持有一个资源,如果一个进程或线程占用了一个资源,其他进程或线程就无法访问该资源。

2. 请求与保持:进程或线程在等待其他进程或线程所拥有的资源时,继续持有已经占用的资源。

3. 不可抢占:已经占用了某个资源的进程或线程不能被其他进程或线程抢占。

4. 循环等待:多个进程或线程形成一个循环等待资源的关系,每个进程或线程都在等待下一个进程或线程所拥有的资源。

死锁的解决方法包括:

1. 预防:通过破坏四个必要条件中的一个或多个条件来预防死锁的发生。

2. 避免:在资源分配的时候,使用一种资源分配算法来避免可能引发死锁的情况。

3. 检测与恢复:周期性地检测系统中是否存在死锁,如果发现死锁,则采取恢复策略(如抢占资源或终止进程)解除死锁。

4. 忽略:有些系统选择忽略死锁的发生,因为死锁发生的概率较低,解除死锁所需的系统开销较大。

package Thread1;public class MyThread extends Thread {static   Object objA = new Object();static Object objB = new Object();@Overridepublic void run() {while (true) {if ("线程A".equals(getName())) {synchronized (objA) {System.out.println("线程A拿到了A锁,准备拿锁");//A锁要拿到B锁才能继续synchronized (objB) {System.out.println("线程A拿到了B锁,顺利执行完一轮");}}}else if ("线程B".equals(getName())) {if ("线程B".equals(getName())) {synchronized (objB) {System.out.println("线程B拿到了B锁,准备拿A锁");//B锁要拿到A锁才能继续synchronized (objA) {System.out.println("线程B拿到了A锁,顺利执行完一轮");}}}}}}
}

这就是死锁, A锁要拿到B锁才能继续,B锁要拿到A锁才能继续

等待唤醒机制

等待唤醒机制(Wait/Notify Mechanism)是指线程之间的一种协作机制,用于解决多线程并发执行中的同步与通信问题。

在等待唤醒机制中,一个线程可以调用wait()方法进入等待状态,同时释放对象锁;而另一个线程则可以调用notify()或者notifyAll()方法来唤醒处于等待状态的线程,并使其进入就绪状态,以便于执行。

等待唤醒机制本质上是基于对象的监视器(Monitor)实现的。每个对象都有一个与之关联的监视器,用于控制对该对象的访问。当一个线程调用了该对象的wait()方法后,该线程就会释放该对象的监视器,并进入等待队列,直到被其他线程调用notify()或者notifyAll()方法来唤醒。

等待唤醒机制常用于生产者-消费者模型、读写锁模型等场景。通过等待唤醒机制,线程之间可以协调合作,实现数据的安全共享与交换。

void wait()        当前线程等待,直到被其他线程唤醒
void notify()        随机唤醒单个线程
void notifyAll()        唤醒所有线程 

写个例子

Desk类:

package waitandnotify;public class Desk {//是否有面条 0:没有  1:有public static int foodFlag=0;//总个数public static int count=10;//锁对象public static Object lock=new Object();}

Foodie类

package waitandnotify;public class Foodie extends Thread{@Overridepublic void run() {///循环while(true){// 同步代码块synchronized (Desk.lock){//   判断共享数据是否到了末尾(到了末尾)if (Desk.count==0){break;}else{// 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)//先判断桌子上是否有面条if (Desk.foodFlag==0){//如果没有就等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//把吃的总数-1Desk.count--;//如果有就开吃System.out.println("正在吃面条,还能吃"+Desk.count+"碗");//吃完之后唤醒厨师继续做Desk.lock.notifyAll();//修改桌子的状态Desk.foodFlag=0;}}}}}
}

Cook类

package waitandnotify;public class Cook extends Thread{@Overridepublic void run() {while (true){synchronized (Desk.lock){if(Desk.count==0){break;}else {//判断桌子上是否有食物if(Desk.foodFlag==1) {// 如果有,就等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {// 如果没有,就制作食物System.out.println("厨师做了一碗面条");// 修改桌子上的食物状态Desk.foodFlag=1;//叫醒消费者开吃Desk.lock.notifyAll();}}}}}
}

测试类

package waitandnotify;public class ThreadDemo {public static void main(String[] args) {Cook c=new Cook();Foodie f=new Foodie();c.setName("厨师");f.setName("吃货");c.start();f.start();}
}

阻塞队列 

阻塞队列(Blocking Queue)是一种特殊的队列,其在插入和删除元素时,当队列已满或为空时会阻塞等待。阻塞队列常用于多线程编程中,用于实现线程间的安全通信。

阻塞队列的主要特点是:当队列为空时,从队列中取出元素的操作将会被阻塞,直到队列中有新的元素被插入;当队列已满时,将元素插入队列的操作将会被阻塞,直到队列中有空位。

阻塞队列有多种实现方式,常见的有:
1. ArrayBlockingQueue:基于数组实现的有界阻塞队列,按照 FIFO(先进先出)的顺序对元素进行存取。
2. LinkedBlockingQueue:基于链表实现的可选有界或无界阻塞队列,按照 FIFO 的顺序对元素进行存取。
3. SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的删除操作,反之亦然。
4. PriorityBlockingQueue:基于优先级堆实现的阻塞队列,元素按照优先级进行存取。

阻塞队列的使用可以简化多线程编程中的线程同步操作,保证线程安全,并提供了一种有效的方式来实现生产者-消费者模型。

 线程的状态

线程池

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象

public static ExecutorService newCachedThreadPool()创建一个没有上限的线程池

MyRunnable类:

package threadpool;public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"-----"+i);}}
}

测试:

package threadpool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {//获取线程池对象ExecutorService pool1 = Executors.newCachedThreadPool();//提交任务pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());}
}


可以看到有6个线程

public static ExecutorService newFixedThreadPool (int nThreads)创建有上限的线程池

测试

package threadpool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo2 {public static void main(String[] args) {//获取线程池对象ExecutorService pool1 = Executors.newFixedThreadPool(3);//提交任务pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());}
}

只有三个线程

线程池主要核心原理

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

自定义线程池

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,时间的单位,任务队列,创建线程工厂,任务的拒绝策略);

参数一:核心线程数量        不能小于θ        
参数二:最大线程数        不能小于等于0,最大数量 >= 核心线程数量
参数三:空闲线程最大存活时间                不能小于θ
参数四:时间单位        用TimeUnit指定
参数五:任务队列                不能为null
参数六:创建线程工厂        不能为null
参数七:任务的拒绝策略        不能为null

package threadpool;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolDemo3 {public static void main(String[] args) {ThreadPoolExecutor pool=new ThreadPoolExecutor(3,//核心线程数量,能小于06,//最大线程数,不能小于0,最大数量>=核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位秒new ArrayBlockingQueue<>(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略);}
}

不断的提交任务,会有以下三个临界点:

  1. 当核心线程满时,再提交任务就会排队
  2. 当核心线程满,队伍满时,会创建临时线程
  3. 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略 

任务拒绝策略

ThreadPoolExecutor.AbortPolicy        丢弃任务并抛出ReiectedExecutionException异常 
ThreadPoolExecutor.DiscardPolicy        丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardoldestPolicy  抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy        调用任务的run()方法绕过线程池直接执行 

最大并行数

多线程的最大并行数理想情况下应为CPU核心数的两倍,即2N,在IO密集型运算中;而在CPU密集型运算中,则建议设置为CPU核心数加一,即N+1

多线程技术是现代计算机编程中不可或缺的一部分,尤其是在处理高并发、高性能需求时,合理设置多线程的最大并行数能显著提高程序的效率和稳定性。这涉及到两种主要的任务类型:IO密集型和CPU密集型。对于IO密集型任务,由于大量的时间花在等待IO操作(如网络请求或数据库查询)上,因此可以设置更多的线程以保持CPU的高效利用。相反,CPU密集型任务则主要集中在处理器运算上,过多的线程反而可能导致频繁的上下文切换,降低效率。


这一期就到这里啦

努力遇见更好的自己!!!

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

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

相关文章

数据可视化作业二:中国城市地铁数据可视化

目录 作业要求 一、绘制每个城市站点数量柱状图&#xff08;降序排列&#xff09; 1.1 每个城市站点数量统计 1.1.1 代码展示 1.1.2 统计结果展示 1.2 柱状图绘制 1.2.1 代码实现 1.2.2 绘制结果 二、绘制上海市地铁线路站点数饼状图 2.1 数据处理 2.2 代码实现 2.3…

实际二分搜索(写出函数,再用二分搜索法找左右边界 画图理解

实际二分搜索&#xff08;写出函数&#xff0c;再用二分搜索法找左右边界 看到最大值的最小化&#xff0c;左边界&#xff0c;最小化的最大值&#xff0c;右边界 画图理解 爱吃香蕉的珂珂 class Solution {public int minEatingSpeed(int[] piles, int h) {int left1,right10…

Mysql 8.3.0 安装

Mysql 8.3.0 安装地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 下载链接&#xff1a;https://downloads.mysql.com/archives/get/p/23/file/mysql-8.3.0-linux-glibc2.28-x86_64.tar.xz 解压&#xff1a; tar -xvf mysql-8.3.0-linux-glib…

sql资料库

1、distinct(关键词distinct用于返回唯一不同的值)&#xff1a;查询结果中去除重复行的关键字 select distinct(university) from user_profile select distinct university from user_profile distinct是紧跟在select后面的&#xff0c;不能在其他位置&#xff0c;不然就…

【Linux】I/O多路复用模型 select、poll、epoll

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;Linux系列专栏&#xff1a;Linux基础 &#x1f525; 给大家…

多模块存储器

随着计算机技术的发展&#xff0c;处理的信息量越来越多&#xff0c;对存储器的速度和容量要求也越来越高&#xff1b;而且随着CPU性能的不断提高、IO设备数量不断增加&#xff0c;导致主存的存取速度已经称为了整个计算机系统的性能瓶颈。这就要求我们必须提高主存的访问速度。…

令人震撼的人类智慧的科学领域-AI技术

AI&#xff0c;全称为人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;是一门致力于让机器模仿人类智慧的科学领域。其核心技术涵盖了机器学习、自然语言处理、计算机视觉及专家系统等多个方面。AI旨在开发能够感知环境、进行逻辑推理、自主学习并做出决策…

【VMware】VMware虚拟机安装_配置_使用教程

一、准备工作 1、下载VMware软件&#xff1a;访问VMware官方网站&#xff0c;下载适合你操作系统的VMware Workstation Pro安装包。 下载地址&#xff1a;VMware Desktop Hypervisors for Windows, Linux, and Mac 2、准备操作系统镜像文件&#xff1a;根据你想要在虚拟机中安…

红黑树(数据结构篇)

数据结构之红黑树 红黑树(RB-tree) 概念&#xff1a; 红黑树是AVL树的变种&#xff0c;它是每一个节点或者着成红色&#xff0c;或者着成黑色的一棵二叉查找树。对红黑树的操作在最坏情形下花费O(logN)时间&#xff0c;它的插入操作使用的是非递归形式实现红黑树的高度最多是…

thread model线程模型

Concurrency并发 vs. Parallelism执行 Many-to-One GNU Portable Threads 多对一即多个用户线程映射到单个内核线程 该模式用的少 一个线程阻塞会导致所有线程阻塞 多线程可能无法在多核系统上并行运行&#xff0c;因为同一时间内核中可能只有一个线程 Concurrent executio…

探索FlowUs息流:个人和团队知识管理解决方案|FlowUs稳定保障你的笔记安全无忧

FlowUs息流&#xff1a;稳定运营保障你的笔记安全无忧 在知识管理工具的选择上&#xff0c;稳定性是用户最关心的问题之一。FlowUs息流以其稳定的运营记录&#xff0c;为用户提供了一个可靠的工作环境。我们深知&#xff0c;一个知识管理平台的稳定性直接影响到团队的生产力和…

3ds MAX 2024版资源包下载分享 3ds Max三维建模软件资源包下载安装

3DSMAX凭借其强大的功能和广泛的应用领域&#xff0c;吸引了无数创作者的青睐。 在游戏制作领域&#xff0c;3DSMAX展现出了无可比拟的优势。从细腻的角色建模到宏大的场景搭建&#xff0c;再到逼真的动画效果和渲染&#xff0c;它都能轻松应对&#xff0c;为游戏世界注入了生动…

“开放”的大模型到底有多“开放”?!

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

java面向对象(上)

一.面向对象与面向过程 1.面向过程 面向过程(procedure Oriented Programming),简称POP,主要思想就是将问题分解成一个个步骤去解决,把这个步骤称为函数. 典型语言:C语言 优点:可以大大简化代码 缺点:当代码量过大时,不方便维护 2.面向对象 面向对象(Object Oriented Pr…

Harbor 源码编译arm版本镜像

1. 先准备一个国外的arm服务器&#xff0c;&#xff08;使用国内的也行&#xff0c;只是有时候下载依赖会断&#xff0c;需要科学上网&#xff09; 2. git clone harbor的源码包&#xff0c;切换到要编译的分支 3. 修改harbor/Makefile Makefile里面的这些字段设置为true&…

四川财谷通信息技术抖音小店信誉之选,购物新体验

在当今数字化浪潮的推动下&#xff0c;电商平台的兴起为人们的生活带来了极大的便利。而在众多电商平台中&#xff0c;抖音小店以其独特的社交属性和便捷的购物体验&#xff0c;逐渐赢得了消费者的青睐。四川财谷通信息技术有限公司旗下的抖音小店&#xff0c;更是凭借其可靠的…

记录SpringBoot启动报错解决

记录SpringBoot启动报错解决 报错现场 Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the following:If you want an embedde…

33.获取入口点

上一个内容&#xff1a;32.双击列表启动目标游戏 前置知识 25.入口点注入&#xff08;查看pe头&#xff09;、32.双击列表启动目标游戏 以它的代码为基础进行修改 效果图&#xff1a; 代码实现&#xff1a;原理通过读文件流的方式把文件加载到内存中然后解析pe结构 void CWnd…

uniapp 实人认证

首先Dcloud创建云服务空间&#xff0c;开启一键登录并充值 下一步 1. 右键项目 》 创建uniCloud云开发环境 》右键uniCloud》关联云服务空间 2. cloudfunctions右键 新建云函数&#xff0c;任意命名&#xff08;例&#xff1a;veify&#xff09;&#xff0c;然后右键项目》管…

Python 基础:异常

目录 一、异常概念二、处理异常2.1 抛出异常2.2 使用 try-except 代码块2.3 使用 try-except-else 代码块2.4 静默失败 三、总结 遇到看不明白的地方&#xff0c;欢迎在评论中留言呐&#xff0c;一起讨论&#xff0c;一起进步&#xff01; 本文参考&#xff1a;《Python编程&a…