当前位置: 首页 > news >正文

Java多线程<三>常见的多线程设计模式

多线程的设计模式

两阶段线程终止

image-20220522205120212

  • park方法

image-20220522205104932

  • interrupted() 会让他失效。

  • 使用volatile关键字进行改写

image-20220522210245081

单例模式 双锁检测

保护性暂停

image-20220529184223468

  • 实现1:
package threadBase.model;/*** @author: Zekun Fu* @date: 2022/5/29 19:01* @Description:* 保护性暂停,* Future 中get方法的实现原理*/
public class GuardedObject {private Object response;// 获取结果public Object get() {synchronized (this) {// 等待结果while (response == null) {try {this.wait();} catch (Exception e) {e.printStackTrace();}}}return response;    // 这里自然释放锁}public void complete(Object response) {synchronized (this) {this.response = response;this.notifyAll();}}
}
  • 实现2:设置超时
package threadBase.model;import java.util.concurrent.ThreadLocalRandom;/*** @author: Zekun Fu* @date: 2022/5/29 19:01* @Description:* 保护性暂停,* Future 中get方法的实现原理***/
public class GuardedObject {private Object response;public Object get(long timeout) {synchronized (this) {long begin = System.currentTimeMillis();long passedTime = 0;while(response == null) {long waitTime = timeout - passedTime;   // 这里是为了防止虚假唤醒if (waitTime <= 0) {break;}try {this.wait(waitTime);} catch (Exception e) {e.printStackTrace();}System.out.println("被唤醒了");passedTime = System.currentTimeMillis() - begin;}return response;}}// 获取结果public Object get() {synchronized (this) {// 等待结果while (response == null) {try {this.wait();} catch (Exception e) {e.printStackTrace();}}return response;    // 这里自然释放锁}}public void complete(Object response) {synchronized (this) {this.response = response;this.notifyAll();}}public static void main(String[] args) {GuardedObject go = new GuardedObject();new Thread(()-> {// 等待两秒Object response = go.get(2000);System.out.println("结果是" + response);}).start();new Thread(()-> {try {// 3s才进行返回Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}go.complete(new Object());}).start();// 虚假唤醒new Thread(()->{try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}go.complete(null);}).start();}
}
  • Join的实现原理

image-20220529191720553

弱鸡版本的生产者消费者

  • 保护性暂停协议的扩展。
  • 解决线程之间的同步问题。
package threadBase.model;import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadLocalRandom;/*** @author: Zekun Fu* @date: 2022/5/29 19:01* @Description:* 保护性暂停,* Future 中get方法的实现原理** 写信者和收信者1:1实现模型。* 写信的人和收信的人是一一对应的关系。* 两个线程通过一个唯一的id确定彼此的身份。** 1. 解耦了收信人线程和写信人线程* 2. 防止写信人线程用完对象不销毁。* 3. MailBoxes获取之后应该销毁对象。*** 多个线程之间速度不匹配,所以使用消息队列。也就是生产者消费者模型。* 这里的停止等待也可以看作是一种一对一的解决速度不匹配问题的机制。** 加上MailBox并没有改变这个的本质,只是方便编码了而已,就是把future* 放入了一个公共的消息队列,然后消费者进行取出。** 可以看作是弱鸡版的生产者消费者模型。* 具有缓解速度不匹配问题的机制,但是必须要实现一对一的模型。***/class MailBoxes {           // 消息队列机制private static Map<Integer, GuardedObject> boxes = new Hashtable<>();private static int id = 1;// 产生唯一的idprivate static synchronized int generateId() {return id++;}public static GuardedObject createGuardedObject() {GuardedObject go = new GuardedObject(generateId());boxes.put(go.getId(), go);return go;}public static Set<Integer> getIds() {return boxes.keySet();}public static GuardedObject getGuardedObject(int id) {return boxes.remove(id);        // 使用remove方法,防止内存泄漏}
}/*
*
*   消费者线程消费特定的future也就是GuardObject。
* */class ReadPeople extends Thread{    // 生产者线程@Overridepublic void run() {//  收信// 1. 创建GuardedObject guardedObject = MailBoxes.createGuardedObject();System.out.println(Thread.currentThread().getName() + "等待收信 id:" + guardedObject.getId());Object mail = guardedObject.get(5000);System.out.println(Thread.currentThread().getName() + "受到信:" + guardedObject.getId() + " 内容:" + mail);}
}/*
*
* 生产者线程,生产特定的Future
**/
class WriteMan extends Thread{          // 消费者线程private int id;private String mail;WriteMan(int id, String mail) {this.id = id;this.mail = mail;}@Overridepublic void run() {GuardedObject guardedObject = MailBoxes.getGuardedObject(id);System.out.println(Thread.currentThread().getName() + "写信 id = " + id +" 内容:" + mail);guardedObject.complete(mail);}
}public class GuardedObject {        // future任务机制private int id;private Object response;public GuardedObject(int id) {this.id = id;}public int getId() {return id;}public Object get(long timeout) {synchronized (this) {long begin = System.currentTimeMillis();long passedTime = 0;while(response == null) {long waitTime = timeout - passedTime;   // 这里是为了防止虚假唤醒if (waitTime <= 0) {break;}try {
//                    System.out.println("等待中..");  // 如果被虚假唤醒this.wait(waitTime);} catch (Exception e) {e.printStackTrace();}
//                System.out.println("被唤醒了"); // 如果被虚假唤醒passedTime = System.currentTimeMillis() - begin;}return response;}}// 获取结果public Object get() {synchronized (this) {// 等待结果while (response == null) {try {this.wait();} catch (Exception e) {e.printStackTrace();}}return response;    // 这里自然释放锁}}public void complete(Object response) {synchronized (this) {this.response = response;this.notifyAll();}}public static void main(String[] args) throws InterruptedException {
//        GuardedObject go = new GuardedObject(1);
//        new Thread(()-> {
//            // 等待两秒
//            Object response = go.get(2000);
//            System.out.println("结果是" + response);
//        }).start();
//
//        new Thread(()-> {
//            try {
//                // 3s才进行返回
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            go.complete(new Object());
//        }).start();
//
//        // 虚假唤醒
//        new Thread(()->{
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            go.complete(null);
//
//        }).start();// 模拟写信和收信的过程// 1. 三个收信的人 (消费者)for (int i = 0; i < 3; i++) {ReadPeople p = new ReadPeople();p.start();}// 2. 三个写信人 (必须对应三个写信者)Thread.sleep(1000);for (int idx: MailBoxes.getIds()) {new WriteMan(idx, "内容" + idx).start();}}
}

生产者消费者模型(终止协议修改)

1. 手动枷锁实现

  • 首先枷锁中的wait应该使用while循环
  • 其次MsgQue应该抛出异常,而不是捕捉。这样终止线程的决定权就在线程那里,而不是必须等消费完最后一个才进行。
  • 而放入队列的异常可以放在MsgQue或者消费者线程中,因为不管怎么样,生产的都需要放入队列中。
package threadBase.model;import java.util.LinkedList;
import java.util.List;
import java.util.Queue;/*** @author: Zekun Fu* @date: 2022/5/31 15:48* @Description: 生产者消费者,使用自己加锁实现*/
public class MessageQue {LinkedList<Message>que = new LinkedList<>();int capcity;public MessageQue(int capcity) {this.capcity = capcity;}public void put(Message x) throws InterruptedException{synchronized (que) {while (que.size() >= capcity) {System.out.println("队列已满," + Thread.currentThread().getName() + "等待");que.wait();}que.addLast(x);         // 尾部添加que.notifyAll();}}public Message get() throws InterruptedException{synchronized (que) {while (que.size() == 0) {que.wait();}Message msg = que.removeFirst();        // 头部取出que.notifyAll();return msg;}}public static void main(String[] args) throws InterruptedException {MessageQue mq = new MessageQue(2);for (int i = 0; i < 3; i++) {int idx = i;new Thread(()->{System.out.println("生产者线程" + idx + "生产完成");try {mq.put(new Message("消息" + idx));} catch (InterruptedException e) {e.printStackTrace();}},"生产者线程" + i).start();}Thread t = new Thread(()-> {Thread cur = Thread.currentThread();while (true) {if (cur.isInterrupted()) {break;}try {Thread.sleep(1000);     // 每隔1s消费一次Message msg = mq.get();System.out.println("消费" + msg.getMsg());} catch (InterruptedException e) {cur.interrupt();System.out.println("停止消费");}}}, "消费者线程");t.start();Thread.sleep(4000);t.interrupt();}
}class Message {private String msg;public Message(String mgs) {this.msg = mgs;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

2. 使用Semphore实现

package threadBase.model;import java.util.LinkedList;
import java.util.concurrent.Semaphore;/*** @author: Zekun Fu* @date: 2022/5/31 16:30* @Description: 使用Semaphore实现****/
public class MessageQue2 implements MesQ{private Semaphore mutex = new Semaphore(1);private Semaphore full;private Semaphore empty;LinkedList<Message>list = new LinkedList<>();public MessageQue2(int capcity) {full = new Semaphore(capcity);empty = new Semaphore(0);}@Overridepublic void put(Message msg) throws InterruptedException {full.acquire();mutex.acquire();list.addLast(msg);mutex.release();empty.release();}@Overridepublic Message get() throws InterruptedException {empty.acquire();mutex.acquire();Message ans = list.removeFirst();mutex.release();full.release();return ans;}public static void main(String[] args) {Test.testMesQ(new MessageQue2(2));}
}

哲学家进餐问题

  • 参考博客

哲学家进餐问题:

  1. 哲学家进餐,不应该有阻塞的线程,因为线程阻塞的条件是等待其他线程的完成通知。

1. 锁住房子破坏了请求保持

请求与保持,就是线程在运行期间,拥有一部分资源,但是仍旧需要更多的资源才能继续运行。

简单的办法,就是采用静态分配的方式,首先把所有的资源都分配好,程序就不会在进行请求了。

简单办法2,就是只有当能够同时拿到两双筷子的时候,才进行分配,如果没有同时拿到两双筷子,就直接放下。

缺点就是:如果程序运行时间很长,而某些资源虽然用的很快就用完了,但是也得等到程序运行完成之后才能进行释放。导致资源利用效率很低。同时也会导致某些进程的饥饿。

第二种的缺点很明显,拿起筷子和放下筷子都是需要消耗cpu和存储资源的,如果拿起放下时间很长,那么就会导致性能低下,资源利用效率低,同时有可能导致活锁问题。

Java中可以使用Mutex加上Synchornized进行资源的静态分配。也就是先设置一个房间。只允许其中的一部分人进去。

img

import java.util.concurrent.Semaphore;public class MealOfPhilosopher {static Semaphore[] chopsticks = new Semaphore[5];static Semaphore mutex = new Semaphore(1);static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {mutex.acquire();chopsticks[j].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");mutex.release();System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}

2. 规定顺序破坏了循环等待

方法是,申请资源一定要按照某种顺序进行。比如设备的id进行从小到达的申请这种。

缺点是,资源的扩展性不好,如果新来了资源,上面的申请逻辑就需要改变。同时因为线程申请资源和使用资源的顺序可能不一致,从而导致请求到的资源无法投入使用的情况,从而导致资源的利用效率低

实现方法1:奇数的只能拿左手边的,偶数的只能拿右手边的

实现方法2:

img

3. 尝试上锁破坏了不可剥夺

img

缺点是,代价很大,可能线程已经运行了一半了,又得重新运行。

实现就是,使用tryAquire的方式获取锁。而不是aquire的方式。

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;public class MealOfPhilosopher {static final Semaphore[] chopsticks = new Semaphore[5];static final Semaphore mutex = new Semaphore(1);static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");if (!chopsticks[j].tryAcquire(10, TimeUnit.SECONDS))System.out.println(Thread.currentThread().getName() + "等待了好长时间,他只好放下他左边的" + j + "号筷子");System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");if (!chopsticks[(j + 1) % 5].tryAcquire(10, TimeUnit.SECONDS))System.out.println(Thread.currentThread().getName() + "等待了好长时间,他只好放下他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在吃饭。");System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}
  • 实现而使用Reentrantlock
package threadBase.model;import leetcode.Philosophiers;import java.util.TreeMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;/*** @author: Zekun Fu* @date: 2022/6/1 14:59* @Description: 哲学家进餐问题,* 破坏不可剥夺 -- 使用tryLock* 破坏请求保持 -- 静态分配* 破坏循环等待 -- 规定顺序。可能会导致某一个线程饥饿。*      因为有的线程和两个人竞争,而有的线程在一个时刻只和一个人竞争**      实现细节:*      1. 筷子可以继承锁变量*      2. 可以使用Semphore实现*      3. 可以使用ReentrantLock 实现的可以说是无锁的,因为线程一直处于就绪和执行装态。**      4. 为什么不进入等待队列中呢?*          因为这不是一个同步问题,没有线程之间的协作,没有一个线程通知另外一个线程这种事情。*          也就是说,不会有人通知他醒过来。*          所以他需要不断的死循环去尝试,去抢筷子。**/class Chopstic extends ReentrantLock {}
public class Philosopher extends Thread {Chopstic left;Chopstic right;public Philosopher(Chopstic left, Chopstic right) {this.left = left;this.right = right;}@Overridepublic void run() {Thread t = Thread.currentThread();while(true) {if (t.isInterrupted()) {System.out.println(t.getName() + "嗝屁了");break;}if (left.tryLock()) {       // 如果拿到了左筷子try {if (right.tryLock()) {  // 尝试拿右筷子, 没拿到try {eat();} finally {right.unlock();     // 如果拿到了,吃饭,放下锁}}}finally {left.unlock();}}try {Thread.sleep(1000);} catch (InterruptedException e) {t.interrupt(); // 重新设置打断标记}}}public void eat() {Thread t = Thread.currentThread();System.out.println(t.getName() + "正在吃饭...");}public static void main(String[] args) throws InterruptedException {Chopstic[] chopstics = new Chopstic[5];for (int i = 0; i < 5; i++) chopstics[i] = new Chopstic();String[] names = {"阿基米德","柏拉图","牛顿","柯西","亚里士多德"};Philosopher[] ps = new Philosopher[5];for (int i = 0; i < 5; i++) {Philosopher p = new Philosopher(chopstics[i], chopstics[(i + 1) % 5]);p.setName(names[i]);p.start();ps[i] = p;}Thread.sleep(10000);for (int i = 0; i < 5; i++) {ps[i].interrupt();}}
}

相关文章:

Java多线程<三>常见的多线程设计模式

多线程的设计模式 两阶段线程终止 park方法 interrupted() 会让他失效。 使用volatile关键字进行改写 单例模式 双锁检测 保护性暂停 实现1&#xff1a; package threadBase.model;/*** author: Zekun Fu* date: 2022/5/29 19:01* Description:* 保护性暂停&#xff0c;* …...

JavaScript 基础二part1.运算符:赋值、一元、比较、逻辑运算符

JavaScript 基础二 1.1 赋值运算符1.2 一元运算符自增运算符的用法&#xff1a;例题 1.3 比较运算符不同类型间的比较严格相等对 null 和 undefined 进行比较 1.4 逻辑运算符例题 1.5 运算符优先级 1.1 赋值运算符 赋值运算符&#xff1a;对变量进行赋值的运算符 已经学过的赋…...

Linux 进程(八) 进程的退出码

main 函数的返回值叫做进程的退出码。当进程成功退出的时候&#xff0c;我们一般用0来表示。进程失败的时候一般用非零来表示。我们使用不同的数字来表示进程退出时不同的失败原因。 我们查看系统的有多少退出码以及其含义时需要用到strerror() 他的头文件和用法如下。 通过一…...

Go语言中支持的internal目录配置与组织内私网包配置详解

Go 中的内部包 这里可能会有歧义 可能是 Go 的 internal 目录中的包也可能是指内部开发的包 函数和变量的可见性 对于函数和变量而言&#xff0c;有如下规则&#xff1a;1 &#xff09;小写字母开头的函数变量结构体只能在本包内访问2 &#xff09;大写字母开头的函数变量结…...

如何使用Nmap加强网络安全?

Nmap是Network Mapper&#xff08;网络映射器&#xff09;的缩写&#xff0c;是一个用于端口和IP扫描以及应用程序检测的开源工具。网络和系统管理员将其用于清点网络资产、管理服务升级计划和监视服务正常运行时间。 起初&#xff0c;它是作为一款Linux工具而开发的&#xff…...

LeetCode 2487. 从链表中移除节点:单调栈

【LetMeFly】2487.从链表中移除节点&#xff1a;单调栈 力扣题目链接&#xff1a;https://leetcode.cn/problems/remove-nodes-from-linked-list/ 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 示例 1&#xff1a; 输…...

LabVIEW在高精度机器人视觉定位系统中的应用

在现代工业自动化中&#xff0c;精确的机器人视觉定位系统对于提高生产效率和产品质量至关重要。LabVIEW软件&#xff0c;以其卓越的图像处理和自动化控制功能&#xff0c;在这一领域发挥着重要作用。本案例将展示LabVIEW如何帮助开发和实现一个高精度的机器人视觉定位系统&…...

Arm CCA机密计算扩展

目录 Realms Realm World和Root World Arm TrustZone扩展和Arm RME之间有什么区别? 在《什么是机密计算?》中所述,Arm CCA允许您在阻止更高特权软件实体(例如Hypervisor)访问的同时部署应用程序或虚拟机(VM)。然而,通常由这些特权软件实体管理内存等资源。在这种情况…...

【Unity入门】热更新框架之xLua

目录 一、xLua概述1.1xLua简介1.2xLua安装 二、Lua文件加载2.1执行字符串2.2加载Lua文件2.3自定义loader 三、xLua文件配置3.1打标签3.2静态列表3.3动态列表 四、Lua与C#交互4.1 C#访问Lua4.1.1 获取一个全局基本数据类型4.1.2 访问一个全局的table4.1.3 访问一个全局的functio…...

大数据Doris(四十五):物化视图选择最优

文章目录 物化视图选择最优 物化视图选择最优 下面详细解释一下第一步最优物化视图是被如何选择出来的。 这里分为两个步骤: 对候选集合进行一个过滤。只要是查询的结果能从物化视图数据计算(取部分行,部分列,或部分行列的聚合)出都可以留在候选集中,过滤完成后候选集合…...

PostgreSQL10数据库源码安装及plpython2u、uuid-ossp插件安装

PostgreSQL10数据库源码安装及plpython2u、uuid-ossp插件安装 1、环境2、安装包下载3、安装3.1 、解压3.2、配置3.3、编译安装3.4 、启动与关闭 4、安装 uuid-ossp 、plpython2u插件5、参考 1、环境 centos 7 、 postgresql 10.19 2、安装包下载 postgres 源码安装包 3、安…...

如何成为ChatGPT 优质Prompt创作者

如何提问&#xff1f; 我想让你成为我的Prompt创作者。你的目标是帮助我创作最佳的Prompt&#xff0c;这个Prompt将由你ChatGPT使用。你将遵循 以下过程&#xff1a;1.首先&#xff0c;你会问我Prompt是关于什么&#xff1f;我会告诉你&#xff0c;但我们需要 通过不断的重复来…...

LeetCode第71题 - 简化路径

题目 以 Unix 风格给出一个文件的绝对路径&#xff0c;你需要简化它。或者换句话说&#xff0c;将其转换为规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&#xff1b;此外&#xff0c;两个点 &#xff08;…&#xff09; 表…...

VSCode上远程调试代码出现的问题

记录一下&#xff1a; 真的是汗流浃背了&#xff0c;师妹叫帮忙如何在VSCode上远程调试代码&#xff0c;一些自己已经经历过的问题&#xff0c;现在已经忘记了。又在网上一顿搜索&#xff0c;这次记录下吧。。。 出现以下问题&#xff1a; 1. 终端界面总是sh-4.4 $ &#xff…...

【langchain】入门初探实战笔记(Chain, Retrieve, Memory, Agent)

1. 简介 1.1 大语言模型技术栈 大语言模型技术栈由四个主要部分组成&#xff1a; 数据预处理流程&#xff08;data preprocessing pipeline&#xff09;嵌入端点&#xff08;embeddings endpoint &#xff09;向量存储&#xff08;vector store&#xff09;LLM 终端&#xff…...

《数据结构、算法与应用C++语言描述》- 平衡搜索树 -全网唯一完整详细实现插入和删除操作的模板类

平衡搜索树 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_34Balanced search tree 概述 本章会讲AVL、红-黑树、分裂树、B-树。 平衡搜索树的应用&#xff1f; AVL 和红-黑树和分裂树适合内部存储的应用。 B-树适合外部存储的…...

网络路由跟踪工具

随着企业网络需求的增长&#xff0c;组织发现监控和管理其网络基础设施变得越来越困难。网络管理员正在转向其他工具和资源&#xff0c;这些工具和资源可以使他们的工作更轻松一些&#xff0c;尤其是在故障排除方面。 目前&#xff0c;网络管理员主要使用简单、免费提供的实用…...

设计模式 七大原则

1.单一职责原则 单一职责原则&#xff08;SRP&#xff1a;Single responsibility principle&#xff09;又称单一功能原则 核心&#xff1a;解耦和增强内聚性&#xff08;高内聚&#xff0c;低耦合&#xff09;。 描述&#xff1a; 类被修改的几率很大&#xff0c;因此应该专注…...

(1)(1.13) SiK无线电高级配置(一)

文章目录 前言 1 监控链接质量 2 诊断范围问题 3 MAVLink协议说明 前言 本文提供 SiK 遥测无线电(SiK Telemetry Radio)的高级配置信息。它面向"高级用户"和希望更好地了解无线电如何运行的用户。 &#xff01;Tip 大多数用户只需要 SiK Radio v2 中提供的基本…...

drf知识--10

接口文档 # 后端把接口写好后&#xff1a; 登录接口&#xff1a;/api/v1/login ---> post---name pwd 注册接口 查询所有图书带过滤接口 # 前后端需要做对接&#xff0c;对接第一个东西就是这个接口文档&#xff0c;前端照着接口文档开发 公司3个人&#xff…...

探索 Vue 实例方法的魅力:提升 Vue 开发技能(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

mysql死锁排查

查看正在进行中的事务 SELECT * FROM information_schema.INNODB_TRX;字段解释trx_id唯一事务id号&#xff0c;只读事务和非锁事务是不会创建id的trx_state事务的执行状态&#xff0c;值一般分为&#xff1a;RUNNING, LOCK WAIT, ROLLING BACK, and COMMITTING.trx_started事务…...

若依项目(ruoy-vue)多模块依赖情况简要分析

主pom文件关键点分析 properties标签声明变量信息&#xff1a;版本号、编码类型、java版本spring-boot依赖形式&#xff1a;spring-boot-dependencies、pom、importdependencies中添加本项目内部模块&#xff0c;同时在modules中声明模块packaging打包选择pom设置打包maven-co…...

【普中开发板】基于51单片机的篮球计分器液晶LCD1602显示( proteus仿真+程序+设计报告+讲解视频)

基于普中开发板51单片机的篮球计分器液晶LCD1602显示 1.主要功能&#xff1a;讲解视频&#xff1a;2.仿真3. 程序代码4. 设计报告5. 设计资料内容清单&&下载链接资料下载链接&#xff08;可点击&#xff09;&#xff1a; 基于51单片机的篮球计分器液晶LCD1602显示 ( pr…...

按照层次遍历结果打印完全二叉树

按照层次遍历结果打印完全二叉树 按照推论结果&#xff1a; l 层首个节点位置 2h-l - 1l 层节点间距&#xff1a;2h-l1 - 1 编码实现 public static<E> void print(BinaryTree<E> tree) {List<List<Node<E>>> levelNodeList levelOrderTraver…...

基于SpringBoot的药店管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的药店管理系统,java项目…...

Java 泛型深入解析

Java 中的泛型是一种强大的编程特性&#xff0c;允许我们编写更加通用和类型安全的代码。本篇博客将深入探讨 Java 泛型的各个方面&#xff0c;包括泛型类、泛型方法、泛型接口以及泛型通配符。 1. 泛型类 首先&#xff0c;让我们看一个简单的泛型类的例子。在下面的代码中&a…...

Apache Doris (六十): Doris - 物化视图

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录...

【javaweb】tomcat9.0中的HttpServlet

2023年12月28日&#xff0c;周四晚上 目录 什么是HttpServlet tomcat中的HttpServlet由谁产生 什么是HttpServlet 在Tomcat中&#xff0c;HttpServlet 是 Java Servlet API 中的一个抽象类&#xff0c;用于简化基于HTTP协议的Servlet的开发。HttpServlet 扩展了 GenericServ…...

数据结构学习笔记——查找算法中的树形查找(B树、B+树)

目录 前言一、B树&#xff08;一&#xff09;B树的概念&#xff08;二&#xff09;B树的性质&#xff08;三&#xff09;B树的高度&#xff08;四&#xff09;B树的查找&#xff08;五&#xff09;B树的插入&#xff08;六&#xff09;B树的删除 二、B树&#xff08;一&#xf…...

wordpress滑动相册/域名注册查询网站

背景当前业务存在以下场景&#xff1a;在一个事务内的最后一步是发送kafka消息&#xff0c;消费端收到通知后读取数据并做处理。但是由于kafka几乎是即时收到消息&#xff0c;导致偶尔出现“在发完kafka和提交事务的间隙&#xff0c;消费端收到了消息并读取到了事务提交前的数据…...

网站域名申请费用/站内优化怎么做

如何制作动态图片在线制作动态图片不完全教程&#xff0c;由动态图片基地【ASQQL.com】原创制作&#xff0c;如有疑问&#xff0c;请通过QQ方式与我们联系&#xff01;联系方式见网站底部&#xff01;动态图片制作步骤分为以下几步&#xff1a;第一步&#xff1a;上传视频或者寻…...

网站模版好建设吗/seo专业优化方法

zabbix 使用进阶&#xff08;五&#xff09;概述&#xff1a;本章内容具体如下&#xff1a;自定义key不带参数&#xff1b;自定义key可传递参数&#xff1b;在agent端自定义可传递参数的key&#xff0c;来获取nginx的状态信息&#xff1a;基于SNMP监控的创建和和使用&#xff1…...

做暧暧视频网站免费/优化师是做什么的

▲点击上方 雷锋网 关注华为已经宣布方舟编译器会从 2019 年全面开源。文 | I/O 2019 年 4 月 11 日&#xff0c;在上海的华为新品发布会上&#xff0c;除了可以拍月亮的华为 P30 系列&#xff0c;余承东还亲自抛出了两项软件层面的“重磅炸弹”&#xff0c;分别是方舟编译器和…...

湖北网站建设服务/网络营销与市场营销的区别

https://blog.csdn.net/java_dotar_01/article/details/76942563 https://blog.csdn.net/jiuduan2009/article/details/51737004转载于:https://www.cnblogs.com/wangshaowei/p/10272467.html...

兖州城乡建设局网站/免费网站提交入口

内省是 Java 语言对 Bean 类属性、事件的一种处理方法(也就是说给定一个javabean对象,我们就可以得到/调用它的所有的get/set方法)。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规…...