1.0
redis配置类
package com.xrh.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory){
RedisTemplate<String, Serializable>redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
//java写进去就是什么防止乱码
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
controller层
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
}
}
2.0
1.0单机版下,在多线程的情况下,会出现问题,单机版加锁synchronized ,当然还有lock,具体用哪个看业务,lock可以设置时间,过了多久就不等了,synchronized就要一直等,容易造成线程挤压
controller层
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
synchronized (this){
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
}
}
}
3.0
当服务部署到分布式后,通过访问nginx来轮询访问两个微服务,用jmeter压力测试,会出现超卖现象,一件物品卖多次,所以加分布式锁。
1.可以用setnx
1.stringRedisTemplate.opsForValue().setIfAbsent这个方法就相当于setnx
2.所谓加锁就是新建一个常量作为key,set方法的value可以是个字符串加线程名
3.setIfAbsent这个方法返回的是布尔类型,如果set成功了,就说明之前没有,代表这侧加锁成功
4.有加锁就要有释放锁,在业务完成的时候删掉这个键值对
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
stringRedisTemplate.delete(REDIS_lOCK);//解锁
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
}
}
4.0
3.0上有加锁也要由解锁,但是如果加锁执行还没有到解锁那一行代码的时候,有异常,所以一定要确保删锁完成,加上finally代码块
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
stringRedisTemplate.delete(REDIS_lOCK);//解锁
}
}
}
5.0
4.0正常异常情况都会释放锁,但是极端情况,还没走到finally,后台服务器宕机了。解决方法setnx的时候加个时间,限定key的有效时间
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value);
stringRedisTemplate.expire(REDIS_lOCK,10L, TimeUnit.SECONDS);//给key加个过期时间
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
stringRedisTemplate.delete(REDIS_lOCK);//解锁
}
}
}
6.0
5.0加锁也加时间了,但是没有原子性,这两行代码不在一行,有可能上面执行了,下面没执行,所以用另一个setnx重载方法
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value,10L,TimeUnit.SECONDS);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
stringRedisTemplate.delete(REDIS_lOCK);//解锁
}
}
}
7.0
6.0下顺利情况没问题,正常业务逻辑5s,有个10s保底。但是如果A线程中的业务突然需要等待,超过10s的保底了,但是redis10s过后就删掉这个锁了。B线程这时候过来了,没有锁,然后抢到了,开始业务。但A线程不卡了,执行下去,把不是自己的锁删了。(张冠李戴,删除了别人的锁)
解决方法,在finally中加层判断,就那个uuid
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value,10L,TimeUnit.SECONDS);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
if (stringRedisTemplate.opsForValue().get(REDIS_lOCK).equalsIgnoreCase(value)){
stringRedisTemplate.delete(REDIS_lOCK);//解锁
}
}
}
}
8.0
7.0下的最后finally的判断和解锁不是原子的,解决方法,官网上有个lua脚本粘贴到finally语块中
如果不用lua脚本的话,考虑redis事务
package com.xrh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value,10L,TimeUnit.SECONDS);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
while (true){
stringRedisTemplate.watch(REDIS_lOCK);
if (stringRedisTemplate.opsForValue().get(REDIS_lOCK).equalsIgnoreCase(value)){//乐观锁
stringRedisTemplate.setEnableTransactionSupport(true);//支持事务
stringRedisTemplate.multi();
stringRedisTemplate.delete(REDIS_lOCK);
List<Object> queued = stringRedisTemplate.exec();
if (queued==null){//说明在删之前改了
continue;
}
}
stringRedisTemplate.unwatch();
break;
}
}
}
}
用lua脚本
先写一个Jredis工具类,Jredis跟stringRedisTemplate都是java连接redis的
package com.xrh.utils;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
private static JedisPool pool;
//创建JedisPool对象
public static JedisPool open(){
if (pool==null){
//创建JedisPool
//创建JedisPoolConfig,给config设置连接池的参数,使用config创建JedisPool
JedisPoolConfig config=new JedisPoolConfig();
//给config设置连接池的参数
config.setMaxTotal(20);//最大线程数
config.setMaxIdle(2);//设置最大空闲数
config.setTestOnBorrow(true);//表示从线程池中获取的对象一定是经过检查可用的,是已经获取的
pool=new JedisPool(config,"192.168.72.129",6379,6000,"666");
}
return pool;
}
//关闭Pool对象
public static void close(){
if (pool!=null){
pool.close();
}
}
}
然后
package com.xrh.controller;
import com.xrh.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value,10L,TimeUnit.SECONDS);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
JedisPool jedisPool = RedisUtil.open();
Jedis jedis = jedisPool.getResource();
//lua脚本
String script="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
try {
Object o = jedis.eval(script, Collections.singletonList(REDIS_lOCK), Collections.singletonList(value));//利用lua脚本判断俩值是否一样
if ("1".equals(o.toString())){//代表相等
System.out.println("---del redis lock ok");
}else {
System.out.println("---del redis lock error");
}
}finally {
if (null!=jedis){
jedis.close();
}
}
}
}
}
9.0
如何确保redis缓存时间大于业务逻辑时间,就是redis分布锁续期。还有就是集群模式下,redis只保证cp(高可用),zookeeper保证ap(一致性),所以redis可能出现数据丢失什么的,这个时候就是Redisson出场的时候,Redlock(redis分布式锁),Redisson就是java写的
配置类加上
@Bean
public Redisson redisson(){
Config config=new Config();
config.useSingleServer().setAddress("redis://192.168.72.129:6379").setDatabase(0);
return (Redisson) Redisson.create(config);
}
controller层
package com.xrh.controller;
import com.xrh.utils.RedisUtil;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class GoodController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String REDIS_lOCK="LOCK";
@Value("${server.port}")
private String serverPort;
@Autowired
private Redisson redisson;
@GetMapping("/buyGoods")
public String buyGoods(){
String value= UUID.randomUUID().toString()+Thread.currentThread().getName();
RLock lock = redisson.getLock(REDIS_lOCK);//!!!!!!!!!跟juc中的lock一样,只不过变成分布式版本的
lock.lock();
try {
//相当于redis中的setnx,因为是set所以也是set k v,key就是指的这把锁,value指的就是谁占用了这把锁
//返回值是布尔类型的
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_lOCK, value,10L,TimeUnit.SECONDS);
if (!flag){
return "抢锁失败";
}
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber=result==null?0:Integer.parseInt(result);
if (goodNumber>0){
int realNumber=goodNumber-1;
stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
System.out.println("成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort);
return "成功买到商品,库存还剩 "+realNumber+"件,服务端口为:"+serverPort;
}else {
System.out.println("购买失败,服务端口为:"+serverPort);
return "购买失败,服务端口为:"+serverPort;
}
} finally {
lock.unlock();
}
}
}
在超高并发下可能会报一个异常,就是当前线程不能解锁(当前的线程和解锁的线程不是同一个),在解锁的时候加两层判断
finally {
if (lock.isLocked()){
if (lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
LRU
利用LinkedHashMap
package com.xrh;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkHashMapLRUCache<k,v> extends LinkedHashMap<k,v>{
private int capacity;//缓存坑位
public LinkHashMapLRUCache(int capacity) {
super(capacity, (float) 0.75,false);//第三个参数是访问顺序,true的话
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
return super.size()>capacity;
}
public static void main(String[] args) {
LinkHashMapLRUCache lruCache=new LinkHashMapLRUCache(3);
lruCache.put(1,"a");
lruCache.put(2,"b");
lruCache.put(3,"c");
System.out.println(lruCache.keySet());
lruCache.put(4,"d");
System.out.println(lruCache.keySet());
lruCache.put(3,"c");
System.out.println(lruCache.keySet());
lruCache.put(3,"c");
System.out.println(lruCache.keySet());
lruCache.put(3,"c");
System.out.println(lruCache.keySet());
lruCache.put(5,"x");
System.out.println(lruCache.keySet());
}
}
//true
//[1, 2, 3]
//[2, 3, 4]
//[2, 4, 3]
//[2, 4, 3]
//[2, 4, 3]
//[4, 3, 5]
//false
//[1, 2, 3]
//[2, 3, 4]
//[2, 3, 4]
//[2, 3, 4]
//[2, 3, 4]
//[3, 4, 5]
纯手写
package com.xrh;
import java.util.HashMap;
import java.util.Map;
/**
* map负责查找,里面封装node,类似AQS
* 步骤:
* 1.构造一个内部类Node
* 2.构造一个双向队列里面装node,相当于AQS
*/
public class LRUCache {
class Node<k,v>{
k key;
v value;
Node<k,v> pre;
Node<k,v> next;
public Node() {
this.pre=this.next=null;
}
public Node(k key, v value) {
this.key = key;
this.value = value;
this.pre=this.next=null;
}
}
//2.
class DoubleLinkedList<k,v>{
Node<k,v>head;
Node<k,v>tail;
//2.1构造方法
public DoubleLinkedList() {
this.head=new Node<>();
this.tail=new Node<>();
head.next=tail;
tail.pre=head;
}
//2.2添加到头
public void addHead(Node<k,v> node){
node.next=head.next;
node.pre=head;
//下面不能用tail表示是因为两个的时候加可以但是多节点的时候就不是尾了,得用head.next.pre表示
head.next.pre=node;
head.next=node;
}
//2.3删除节点
public void removeNode(Node<k,v> node){
node.pre.next=node.next;
node.next.pre=node.pre;
node.next=null;
node.pre=null;
}
//2.4获得最后一个节点
public Node getLast(){
return tail.pre;
}
}
//上面两个内部类写完了
//下面写LRUCache的属性,方法
private int cacheSize;
Map<Integer,Node<Integer,Integer>> map;
DoubleLinkedList<Integer,Integer>doubleLinkedList;
public LRUCache(int cacheSize) {
this.cacheSize = cacheSize;
map=new HashMap<>();
doubleLinkedList=new DoubleLinkedList<>();
}
public int get(int key){
if (!map.containsKey(key)){
return -1;
}
Node<Integer,Integer>node=map.get(key);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node); //妙啊,如果查这个key说明又用到了,删掉原来的,放在头上,表示常用的
return node.value;
}
//更新或者新加
public void put(int key,int value){
if (map.containsKey(key)){
Node<Integer,Integer>node=map.get(key);
node.value=value;
map.put(key,node);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
}else {//如果这个map中没有这个值,有两种情况,如果坑位满了,就要删除最后一个,不满就加入
if (map.size()==cacheSize){
Node<Integer,Integer> lastNode=doubleLinkedList.getLast();
map.remove(lastNode.key);//map也要删
doubleLinkedList.removeNode(lastNode);
}
//才是新增
Node<Integer,Integer>newNode=new Node<>(key,value);
map.put(key,newNode);
doubleLinkedList.addHead(newNode);
}
}
public static void main(String[] args) {
LRUCache lruCache=new LRUCache(3);
lruCache.put(1,1);
lruCache.put(2,2);
lruCache.put(3,3);
System.out.println(lruCache.map.keySet());
lruCache.put(4,4);
System.out.println(lruCache.map.keySet());
lruCache.put(3,3);
System.out.println(lruCache.map.keySet());
lruCache.put(3,3);
System.out.println(lruCache.map.keySet());
lruCache.put(3,3);
System.out.println(lruCache.map.keySet());
lruCache.put(5,5);
System.out.println(lruCache.map.keySet());
}
}