外卖小程序09
目录
- java中的四种引用
- 垃圾回收机制
-
- 介绍
- 垃圾判断
-
- 引用计数法
- 可达性分析法
- 垃圾收集算法
-
- Mark-Sweep(标记-清除算法)
- Coping算法
- Mark-Compact算法
- Generational-Collection算法
- SpringTask
-
- 介绍
- Cron表达式
- 使用步骤
-
- 入门案例
- 需求
-
- 代码实现
-
- 自定义任务类OrderTask
- OrderMapper
- WebSocket
-
- 介绍
- 对比HTTP
- 使用步骤
- 需求
- 代码实现
-
- Controller层
-
- OrderController
- Service层
-
- OrderService
- OrderServiceImpl
- OrderMapper
java中的四种引用
直接上代码
package com.shifan.reference;import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.junit.Test;import java.io.IOException;
import java.lang.ref.*;public class ReferenceTest2 {/* 垃圾回收会调用此方法,如果打印了finalize,说明对象被回收了* @throws Throwable*/@Overrideprotected void finalize() throws Throwable {System.out.println("finalize");}/* 强引用测试* 我们在创建对象时默认使用的引用就是强引用*/@Testpublic void testNormalReference() throws IOException {//r是强引用,被强引用指向的对象是不会被回收的ReferenceTest2 r = new ReferenceTest2();//断开指向,对象将会被回收r = null;//显式调用垃圾回收System.gc();//阻塞当前线程,避免线程结束System.in.read();}/* 测试软引用* 一般用于做缓存* 软引用类SoftReference继承自Reference类* 拥有两个构造方法* public SoftReference(T referent);* public SoftReference(T referent, ReferenceQueue<? super T> q);* 第一个参数为软引用的对象,第二个参数为封装的待回收的对象*/@Testpublic void testSoftReference(){A a = new A();//sr是一个软引用,只有当内存不足时,软引用指向的对象才会被回收SoftReference<A> sr = new SoftReference<A>(a);System.out.println(sr.get());//com.shifan.reference.ReferenceTest2$A@a74868dSystem.gc();System.out.println(sr.get());//com.shifan.reference.ReferenceTest2$A@a74868d}@NoArgsConstructor@AllArgsConstructorclass A{String name;}/* 测试弱引用* 一般用于做容器* 弱引用只要遇到垃圾回收其引用的对象就会被回收* 弱引用所指向的对象若被强引用所引用,遇到垃圾回收时依然会被回收* 弱引用也继承自Reference类,同样拥有两个构造方法,与软引用类似*/@Testpublic void testWeakReference(){WeakReference<A> wr = new WeakReference(new A());System.out.println(wr.get());//com.shifan.reference.ReferenceTest2$A@12c8a2c0System.gc();System.out.println(wr.get());//null}/* 测试虚引用* 虚引用与软引用,弱引用不同之处在于,虚引用只有一个构造方法,其中引入对象和引用队列都是必须传递的* 虚引用的作用是跟踪垃圾收集器手机对象的过程,gc过程中如果遇到了phantomReference,会将该引用存入* ReferenceQueue中让程序员自己处理,当我们调用了ReferenceQueue的poll方法移除了里面的引用之后* 被引用的对象就会变成inactive状态,即可回收状态*/@Testpublic void testPhantom(){ReferenceQueue<A> rq = new ReferenceQueue<>();A a = new A();PhantomReference<A> pr = new PhantomReference<>(a, rq);a = null;System.out.println(pr.get());//null,由于虚引用指向的都是需要被回收的对象,所以此处的get方法一直是返回nullSystem.gc();Reference<A> poll = (Reference<A>) rq.poll();System.out.println(poll);//java.lang.ref.PhantomReference@a74868d,有时为null,有时有地址}
}
垃圾回收机制
介绍
GC(garbage collection,内存自动回收):垃圾回收机制可以有效地防止内存泄漏
在java中,内存管理实际上是对对象的管理,gc通过有向图的方式,跟踪对象正在使用的对象
垃圾判断
引用计数法
通过对象被引用的数量判断对象是否应该被回收,若没有一个引用指向对象,则应该将该对象回收
缺点:无法解决循环引用的问题
可达性分析法
基本思路是以GC Roots的活跃引用为起始点开始搜索,如果没有一条路径可以到达一个对象,则称该对象是不可达的,亦可回收的.
垃圾收集算法
Mark-Sweep(标记-清除算法)
统一标记待回收的对象,然后清理
缺点:会生成很多内存碎片,可能会造成申请较大块内存空间时无法申请成功的情况
Coping算法
将内存划分为两块同样大小的空间,每次使用其中一块,并在垃圾回收时将存活的对象复制到另一片内存中去,然后清理这片内存
缺点:每次只能使用一半的空间,如果存活的对象非常多,复制的代价很大
Mark-Compact算法
标记阶段和Mark-Sweep算法一致,清理阶段此算法将存活的对象集中移动到内存的一端,然后清理存活对象边界外的区域
Generational-Collection算法
核心思想是将内存根据生命周期划分为若干部分,一般分为两部分,一部分用于存放新生代对象,新生代对象具有生命周期相对较短,每次需要回收的数量大的特点,另一部分存放老年代对象,老年代对象具有生命周期长的特点,每次需要回收的对象很少
新生代算法:新生代对象一般使用Coping算法,但并非将内存划分为同样大小的两片空间,而是将内存划分为较大的一块空间Eden和两块较小的空间Survivor,每次使用Eden空间和一块Survivor空间,回收对象前将存活的对象复制到未使用的Sruvivor空间中去,然后清理Eden和使用过的Survivor空间
老年代算法:老年代对象使用Mark-Compact算法实现对象的回收
SpringTask
介绍
Spring框架提供的任务调度工具,可以按照约定的时间自动执行某段代码逻辑
Cron表达式
本质就是一个字符串,通过cron表达式可以定义任务触发的时间
与SpringTask框架组合使用即可实现任务调度
构成:由6或7个域构成,空格隔开,每个域代表不同的含义,分别为秒,分,时,日,月,周,年(可选)
eg:2023年4月13日晚上21点整表示为:0 0 21 13 4 ? 2023
一般日和周不同时设置,设置其中一个,另一个用?表示
cron表达式在线生成器:https://cron.qqe2.com/
通配符:
* 表示所有值;
? 表示未说明的值,即不关心它为何值;
表示一个指定的范围;
, 表示附加一个可能值;
/ 符号前表示开始时间,符号后表示每次递增的值;
cron表达式案例:
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
使用步骤
1.导入SpringTaskMaven坐标(包含于Spring-Context内)
2.在启动类上添加@EnableScheduling注解开启任务调度功能
3.自定义任务调度类
入门案例
在启动类上添加@EnableScheduling注解,编写自定义任务类,运行测试即可
/* 自定义定时任务类*/
@Component
@Slf4j
public class MyTask {/* 定时任务 每隔5秒触发一次*/@Scheduled(cron = "0/5 * * * * ?")public void executeTask(){log.info("定时任务开始执行:{}",new Date());}
}
需求
定时处理订单状态
1.每分钟触发一次待支付订单处理逻辑,将支付超时的订单状态置为已取消
2.每天凌晨一点触发一次派送中订单处理逻辑,将派送超过一小时的订单状态设为已完成
代码实现
自定义任务类OrderTask
/* 定时任务类,定时处理订单状态*/
@Component
@Slf4j
public class OrderTask {@Autowiredprivate OrderMapper orderMapper;/* 处理支付超时订单*/@Scheduled(cron = "0 * * * * ?")//每分钟触发一次public void processTimeoutOrder() {log.info("处理支付超时订单:{}", new Date());LocalDateTime orderTime = LocalDateTime.now().minusMinutes(15);//根据订单状态和下单时间查找订单List<Orders> ordersList = orderMapper.selectByStatusAndOrderTime(Orders.PENDING_PAYMENT, orderTime);if (ordersList != null && ordersList.size() > 0) {//更新订单状态ordersList.forEach(orders -> {orders.setStatus(Orders.CANCELLED);orders.setCancelReason("支付超时,自动取消");orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);});}}/* 处理“派送中”状态的订单*/@Scheduled(cron = "0 0 1 * * ?")//每天凌晨一点触发一次public void processDeliveryOrder() {log.info("处理派送中订单:{}", new Date());LocalDateTime orderTime = LocalDateTime.now().minusHours(1);//根据订单状态和下单时间查找订单List<Orders> ordersList = orderMapper.selectByStatusAndOrderTime(Orders.DELIVERY_IN_PROGRESS, orderTime);if (ordersList != null && ordersList.size() > 0) {//更新订单状态ordersList.forEach(orders -> {orders.setStatus(Orders.COMPLETED);orderMapper.update(orders);});}}
}
OrderMapper
/* 根据状态和下单时间查询订单* @param status* @param orderTime*/@Select("select * from orders where status = #{status} and order_time < #{orderTime}")List<Orders> getByStatusAndOrdertimeLT(Integer status, LocalDateTime orderTime);
WebSocket
介绍
基于TCP的新型网络协议,实现了浏览器和服务器全双工通信----只需进行一次握手即可建立持久性的连接,并进行双向数据传输.
对比HTTP
HTTP是短连接,WebSocket是长连接
HTTP是单向通信,基于请求响应,WebSocket是双向通信
HTTP和WebSocket都是基于TCP协议实现的
缺点:服务器长时间维护长连接成本较大,且各浏览器的支持程度不同,长连接受网络影响较大,需要处理好重连,所以WebSocket只适合在特定场景下使用
使用步骤
1.导入WebSocketMaven坐标
2.编写WebSocketServer服务端组件,用于和客户端通信
3.编写WebSocketConfiguration配置类,注册WebSocket服务端组件
4.编写定时任务类
需求
用户催单,用户催单后通知商家处理
通过WebSocket实现管理端页面和服务端保持长连接状态
当用户点击催单按钮后,调用WebSocket的相关API实现服务端向客户端推送消息
客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报
约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,contenttype 为消息类型,1为来单提醒 2为客户催单
orderId 为订单id
content 为消息内容
代码实现
Controller层
OrderController
@RestController("userOrderController")
@RequestMapping("user/order")
@Api(tags = "用户端订单相关接口")
@Slf4j
public class OrderController {@Autowiredprivate OrderService orderService;/* 用户催单 @param id* @return*/@GetMapping("reminder/{id}")@ApiOperation("用户催单")public Result reminder(@PathVariable Long id) {log.info("用户催单:{}", id);orderService.reminder(id);return Result.success();}
}
Service层
OrderService
/* 用户催单 @param id*/void reminder(Long id);
OrderServiceImpl
/* 用户催单 @param id*/@Overridepublic void reminder(Long id) {//查询订单Orders orders = orderMapper.selectByOrderId(id);//判断订单是否存在if (orders == null) {throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);}//基于WebSocket实现催单HashMap hashMap = new HashMap();hashMap.put("type", 2);hashMap.put("orderId", id);hashMap.put("content", "订单号" + orders.getNumber());webSocketServer.sendToAllClient(JSON.toJSONString(hashMap));}
OrderMapper
/* 根据id查询订单* @param id*/@Select("select * from orders where id=#{id}")Orders getById(Long id);