直接上代码
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
*/
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
}
/**
* 强引用测试
* 我们在创建对象时默认使用的引用就是强引用
*/
@Test
public 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);
* 第一个参数为软引用的对象,第二个参数为封装的待回收的对象
*/
@Test
public void testSoftReference(){
A a = new A();
//sr是一个软引用,只有当内存不足时,软引用指向的对象才会被回收
SoftReference<A> sr = new SoftReference<A>(a);
System.out.println(sr.get());//com.shifan.reference.ReferenceTest2$A@a74868d
System.gc();
System.out.println(sr.get());//com.shifan.reference.ReferenceTest2$A@a74868d
}
@NoArgsConstructor
@AllArgsConstructor
class A{
String name;
}
/**
* 测试弱引用
* 一般用于做容器
* 弱引用只要遇到垃圾回收其引用的对象就会被回收
* 弱引用所指向的对象若被强引用所引用,遇到垃圾回收时依然会被回收
* 弱引用也继承自Reference类,同样拥有两个构造方法,与软引用类似
*/
@Test
public void testWeakReference(){
WeakReference<A> wr = new WeakReference(new A());
System.out.println(wr.get());//com.shifan.reference.ReferenceTest2$A@12c8a2c0
System.gc();
System.out.println(wr.get());//null
}
/**
* 测试虚引用
* 虚引用与软引用,弱引用不同之处在于,虚引用只有一个构造方法,其中引入对象和引用队列都是必须传递的
* 虚引用的作用是跟踪垃圾收集器手机对象的过程,gc过程中如果遇到了phantomReference,会将该引用存入
* ReferenceQueue中让程序员自己处理,当我们调用了ReferenceQueue的poll方法移除了里面的引用之后
* 被引用的对象就会变成inactive状态,即可回收状态
*/
@Test
public 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方法一直是返回null
System.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算法,但并非将内存划分为同样大小的两片空间,而是将内存划分为较大的一块空间Eden和两块较小的空间Survivor,每次使用Eden空间和一块Survivor空间,回收对象前将存活的对象复制到未使用的Sruvivor空间中去,然后清理Eden和使用过的Survivor空间
**老年代算法:**老年代对象使用Mark-Compact算法实现对象的回收
Spring框架提供的任务调度工具,可以按照约定的时间自动执行某段代码逻辑
本质就是一个字符串,通过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.每天凌晨一点触发一次派送中订单处理逻辑,将派送超过一小时的订单状态设为已完成
/**
* 定时任务类,定时处理订单状态
*/
@Component
@Slf4j
public class OrderTask {
@Autowired
private 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);
});
}
}
}
/**
* 根据状态和下单时间查询订单
* @param status
* @param orderTime
*/
@Select("select * from orders where status = #{status} and order_time < #{orderTime}")
List<Orders> getByStatusAndOrdertimeLT(Integer status, LocalDateTime orderTime);
基于TCP的新型网络协议,实现了浏览器和服务器全双工通信----只需进行一次握手即可建立持久性的连接,并进行双向数据传输.
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 为消息内容
@RestController("userOrderController")
@RequestMapping("user/order")
@Api(tags = "用户端订单相关接口")
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 用户催单
*
* @param id
* @return
*/
@GetMapping("reminder/{id}")
@ApiOperation("用户催单")
public Result reminder(@PathVariable Long id) {
log.info("用户催单:{}", id);
orderService.reminder(id);
return Result.success();
}
}
/**
* 用户催单
*
* @param id
*/
void reminder(Long id);
/**
* 用户催单
*
* @param id
*/
@Override
public 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));
}
/**
* 根据id查询订单
* @param id
*/
@Select("select * from orders where id=#{id}")
Orders getById(Long id);