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();}}
}

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

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

相关文章

主流级显卡的新选择,Sparkle(撼与科技)Intel Arc A750兽人体验分享

▼前言 对于玩家而言&#xff0c;英特尔独显的出现不仅打破了NVIDIA与AMD双雄天下的局面&#xff0c;而且旗下的Arc A系列显卡还拥有不俗的做工性能以及颇具优势的价格&#xff0c;无论是升级或者是装新机都非常合适。如果要在Arc A系列当中选一个性能不俗&#xff0c;能够满足…

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题)

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 第一部分&#xff1a;数据安全防护(30%) 第二部分&#xff1a;数据安全管理(30%) 第三部分&#xff1a;数据安全处置(40%) 项目介绍…

pytorch01:概念、张量操作、线性回归与逻辑回归

目录 一、pytorch介绍1.1pytorch简介1.2发展历史1.3pytorch优点 二、张量简介与创建2.1什么是张量&#xff1f;2.2Tensor与Variable2.3张量的创建2.3.1 直接创建torch.tensor()2.3.2 从numpy创建tensor 2.4根据数值创建2.4.1 torch.zeros()2.4.2 torch.zeros_like()2.4.3 torch…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的固定帧率功能的技术背景CameraExplorer如何查看相机固定帧率功能在NEOAPI SDK里通过函数设置相机固定帧率 Baumer工业相机通过NEOAPI SDK设置相机固定…

计算机毕业设计 基于HTML5+CSS3的在线英语阅读分级平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

深度学习 | 编码器-解码器网络、seq2seq模型、束搜索算法

我们知道传统RNN输入和输出数据是等长的&#xff0c;这显然极大限制了他的应用范围。 前面几节我们讲到的循环神经网络的各种变体基本上都在解决一个序列的问题。还有一大类问题涉及到的是两个序列间转换。它是自然语言处理中的一个重要领域&#xff0c;包括机器翻译、语音识别…

【GOLANG】使用插件 Goanno 的方式来对方法、接口、结构体注释模板配置

直接 使用插件 Goanno 的方式来对方法、接口、结构体注释模板配置 1、简单安装 Goanno 插件 File->Settings->Plugins , 搜索 Goanno Normal Method 配置内容如下&#xff1a; // Title ${function_name} // Description ${todo} // Author mumu ${date} ${time} // Par…

速卖通详情API接口技术贴:解锁全球商品信息,引领电商创新潮流

一、概述 速卖通详情API接口是一种应用程序接口&#xff0c;允许开发者访问速卖通平台的商品详情数据。通过使用该API接口&#xff0c;开发者可以获取商品的基本信息、描述、图片等&#xff0c;并将其集成到自己的应用程序或网站中&#xff0c;为用户提供更全面的商品信息。 …

进阶学习——Linux系统服务器硬件认识与RAID磁盘

目录 一、服务器知识补充 1.硬件 2.服务器常见故障 二、认识RAID 1.什么是RAID 2.RAID的优点 3.RAID的实现方式 三、RAID磁盘陈列 1.RAID 0 磁盘陈列介绍——RAID 0 2.RAID 1 磁盘陈列介绍——RAID 1 3.RAID 5 磁盘陈列介绍——RAID 5 4.RAID 6 磁盘陈列介绍——RA…

解决阿里云远程连接yum无法安装问题(Ubuntu 22.04)

解决阿里云远程连接yum无法安装问题&#xff08;Ubuntu 22.04&#xff09; 第一步 进入阿里云远程连接后&#xff0c;尝试安装宝塔面包第二步&#xff1a;尝试更新软件包等一些列操作第三步&#xff1a;完成上述操作之后&#xff0c;尝试安装yum第四步&#xff1a;尝试更换清华…

代数结构与图论

文章目录 图的基本概念欧拉图与哈密顿图树平面图代数系统群与环格与布尔代数 图的基本概念 图的阶&#xff1a;图中的顶点数 &#xff0c;n 个顶点被称为 n 阶图零图&#xff1a;一条边都没有 平凡图&#xff1a;一阶零图基图&#xff1a;将有向图的各条有向边改成无向边所得到…

java springboot将接口查询数据放在系统中 一小时系统更新一次 避免用户访问接口查询数据库缓慢

真到了公司 很多数据库表 特别是常用的功能业务对应的 都是几百万条起步的数据 查询会比较缓慢 那么 我们就可以不用每次都真的查询数据库 例如 我这里有一个接口 通过 封装的 IBookService.list 函数去查询数据库 接口返回是这样的 我们先在启动类 条件装配上 这个接口所在的…

阿里云30个公共云地域、89个可用区、5个金融云和政务云地域

阿里云基础设施目前已面向全球四大洲&#xff0c;公共云地域开服运营30个公共云地域、89个可用区&#xff0c;此外还拥有5个金融云、政务云地域&#xff0c;并且致力于持续的新地域规划和建设&#xff0c;从而更好的满足用户多样化的业务和场景需求。伴随着基础设施的加速投入和…

【Java开发岗面试】八股文—Java基础集合多线程

声明&#xff1a; 背景&#xff1a;本人为24届双非硕校招生&#xff0c;已经完整经历了一次秋招&#xff0c;拿到了三个offer。本专题旨在分享自己的一些Java开发岗面试经验&#xff08;主要是校招&#xff09;&#xff0c;包括我自己总结的八股文、算法、项目介绍、HR面和面试…

pytorch05:卷积、池化、激活

目录 一、卷积1.1 卷积的概念1.2 卷积可视化1.3 卷积的维度1.4 nn.Conv2d1.4.1 无padding 无stride卷积1.4.2 无padding stride2卷积1.4.3 padding2的卷积1.4.4 空洞卷积1.4.5 分组卷积 1.5 卷积输出尺寸计算1.6 卷积的维度1.7 转置卷积1.7.1 为什么被称为转置卷积1.7.2 nn.Con…

Grafana增加仪表盘

1.Grafana介绍 grafana 是一款采用Go语言编写的开源应用&#xff0c;主要用于大规模指标数据的可视化展现&#xff0c;是网络架构和应用分析中最流行的时序数据展示工具&#xff0c;目前已经支持绝大部分常用的时序数据库。 Grafana下载地址&#xff1a;https://grafana.com/g…

Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射

系列文章 一、逆向工程 Sketchup 逆向工程&#xff08;一&#xff09;破解.skp文件数据结构 Sketchup 逆向工程&#xff08;二&#xff09;分析三维模型数据结构 Sketchup 逆向工程&#xff08;三&#xff09;软件逆向工程从何处入手 Sketchup 逆向工程&#xff08;四&#xf…

Java强软弱虚引用

面试&#xff1a; 1.强引用&#xff0c;软引用&#xff0c;弱引用&#xff0c;虚引用分别是什么&#xff1f; 2.软引用和弱引用适用的场景&#xff1f; 3.你知道弱引用的话&#xff0c;能谈谈WeakHashMap吗&#xff1f; 目录 一、Java引用 1、强引用&#xff08;默认支持模式…

05-C++ 类和对象-继承

类与对象-03 继承与派生 1. 继承的概念 c最重要的特征是代码重用&#xff0c;通过继承机制可以利用已有的数据类型&#xff0c;来定义新的数据类型&#xff0c;新的类不仅拥有旧类的成员&#xff0c;还拥有新定义的成员。 一个 B 类继承于 A 类&#xff0c;或称从类 A 派生…

菜鸟网络Java实习一面面经

自我介绍&#xff0c;做过的项目 巴拉巴拉 你项目中用到redis&#xff0c;可以介绍一下为什么使用它吗&#xff1f; 基于内存操作&#xff0c;内存读写速度快。 支持多种数据类型&#xff0c;包括String、Hash、List、Set、ZSet等。 支持持久化。Redis支持RDB和AOF两种持久…