BlockingQueue实现类<二>

本文深入分析了Java并发包中LinkedBlockingQueue的实现原理,重点介绍了其构造方法、offer方法及take方法的具体实现,并通过对比ArrayBlockingQueue突出了其基于链表实现的特点。

这篇文章主要讲LinkedBlockingQueue。阻塞队列使用链表来完成存储,之前分析的ArrayBlockingQueue是用数组。现在看下具体实现会有什么特点,还是从构造方法出发。

   public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        //初始化了一个Node。
        last = head = new Node<E>(null);
    }

跟之前一样,来分析下offer吧。

   public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)
            return false;
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
                enqueue(node);
                c = count.getAndIncrement();
                //再加1,还小于容量,可以释放锁。
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }

//将node 放在链尾的末端。

   private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

//获取head的内容。

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        //写锁。
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
        //没有内容,需要等待。
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            //释放notEmpty锁。
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

来分析下 dequeue()。链表操作。返回了head结点并且head置为空,释放掉。注意写法很考究。

private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head;
        Node<E> first = h.next;
        //这样原来的头部结点的next也指向自身了。原来next持有别的对象,这样容易gc释放。
        h.next = h; // help GC
        //head变为了原来的next。
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

其实跟之前分析的ArrayBlockingQueue差别不大。不过是读写分离,利用链表实现。

public interface CacheService { @Nullable <T> T get(String cacheName, @NonNull String key, @NonNull Class<T> clazz); /** @deprecated */ @Nullable @Deprecated <T> T get(String cacheName, @NonNull String key, @NonNull BiFunction<String, String, T> loadHandle, @NonNull Class<T> clazz); Stream<String> getKeysByPattern(String cacheName, @NonNull String pattern); <T> void set(String cacheName, @NonNull String key, @NonNull T value); <T> void set(String cacheName, @NonNull String key, @NonNull T value, long timeToLive, @NonNull TimeUnit timeUnit); boolean deleteKey(String cacheName, @NonNull String key); long deleteKeyByPrefix(String cacheName, @NonNull String prefix) throws UnsupportedOperationException; boolean expireKey(String cacheName, @NonNull String key, long timeToLive, @NonNull TimeUnit timeUnit); <T> boolean replace(String cacheName, @NonNull String key, @NonNull T oldValue, @NonNull T newValue); boolean cas(String cacheName, @NonNull String key, @NonNull Version value); long getRemainTimeToLive(String cacheName, @NonNull String key); IAtomicLong getAtomicLong(String cacheName, @NonNull String key); <T> Map<String, T> getHash(String cacheName, @NonNull String key, @NonNull Class<T> clazz); @Nullable <T> T getHashValue(String cacheName, @NonNull String key, @NonNull String hashKey, @NonNull Class<T> clazz); <T> T putHashValue(String cacheName, @NonNull String key, @NonNull String hashKey, @NonNull T value); @Nullable <T> T deleteHashValue(String cacheName, @NonNull String key, @NonNull String hashKey); /** @deprecated */ @Deprecated <T> Map<String, T> getTimedHash(String cacheName, @NonNull String key, @NonNull Class<T> clazz); /** @deprecated */ @Deprecated @Nullable <T> T getTimedHashValue(String cacheName, @NonNull String key, @NonNull String hashKey, @NonNull Class<T> clazz); /** @deprecated */ @Deprecated <T> T putTimedHashValue(String cacheName, @NonNull String key, @NonNull String hashKey, @NonNull T value); /** @deprecated */ @Deprecated <T> T putTimedHashValue(String cacheName, @NonNull String key, @NonNull String hashKey, @NonNull T value, long timeToLive, @NonNull TimeUnit timeUnit); /** @deprecated */ @Deprecated @Nullable <T> T deleteTimedHashValue(String cacheName, @NonNull String key, @NonNull String hashKey); <T> boolean addSortedSetValue(String cacheName, @NonNull String key, @NonNull T value, double score); <T> int addSortedSetValueReturnSize(String cacheName, @NonNull String key, @NonNull T value, double score); <T> Collection<T> rangeSortedByScore(String cacheName, @NonNull String key, double min, double max, @NonNull Class<T> clazz); <T> boolean removeSortedSetValue(String cacheName, @NonNull String key, @NonNull T value); <T> Collection<T> pollSortedByScore(String cacheName, @NonNull String key, double min, double max, @NonNull Class<T> clazz); <T> Collection<T> pollSortedByScore(String cacheName, @NonNull String key, double min, double max, int count, @NonNull Class<T> clazz); <T> Collection<T> pollFirst(String cacheName, @NonNull String key, int count, @NonNull Class<T> clazz); <T> Collection<T> pollFirst(String cacheName, @NonNull String key, int count, double min, double max, @NonNull Class<T> clazz); @Nullable <T> T peekFirst(String cacheName, @NonNull String key, @NonNull Class<T> clazz); int getSortedSetSize(String cacheName, @NonNull String key); <T> Set<T> getSet(String cacheName, @NonNull String key, @NonNull Class<T> clazz); <T> List<T> getList(String cacheName, @NonNull String key, @NonNull Class<T> clazz); <T> Queue<T> getQueue(String cacheName, @NonNull String key, @NonNull Class<T> clazz); <T> Deque<T> getDeque(String cacheName, @NonNull String key, @NonNull Class<T> clazz); <T> BlockingQueue<T> getBlockingQueue(String cacheName, @NonNull String key, @NonNull Class<T> clazz); <T> BlockingDeque<T> getBlockingDeque(String cacheName, @NonNull String key, @NonNull Class<T> clazz); } 这是项目里面的缓存接口,我实现的部分(get)有点问题,如下,怎么修改: @Slf4j @Service public class WebSocketResultCacheService { public static final String WEBSOCKET_RESULT = "websocket:result"; private static final int FILE_ID_TIME_OUT = 30; private static final TimeUnit FILE_ID_TIME_OUT_UNIT = TimeUnit.MINUTES; @Autowired private CacheService cacheService; @Autowired private ObjectMapper objectMapper; // Jackson ObjectMapper /** * @param vmsId vmsId * @param taskId taskId * @return */ public <T> getResult(String vmsId, String taskId) { String cacheKey = buildCacheKey(vmsId, taskId); return cacheService.get(WEBSOCKET_RESULT, cacheKey, ); } /** * @param vmsId vmsId * @param taskId taskId * @param dto WebSocketResultDTO */ public <T> void setValue(String vmsId, String taskId, WebSocketResultDTO<T> dto) { String cacheKey = buildCacheKey(vmsId, taskId); // todo 修改缓存名字 RedisKey.WEBSOCKET_RESULT cacheService.set(WEBSOCKET_RESULT, cacheKey, dto, FILE_ID_TIME_OUT, FILE_ID_TIME_OUT_UNIT); } /** * @param vmsId vmsId * @param taskId taskId */ public void delete(String vmsId, String taskId) { String cacheKey = buildCacheKey(vmsId, taskId); // todo 修改缓存名字 RedisKey.WEBSOCKET_RESULT cacheService.deleteKey(WEBSOCKET_RESULT, cacheKey); } private String buildCacheKey(String vmsId, String taskId) { return String.format("websocket:result:%s:%s", vmsId, taskId); } }
10-10
<think>我们面临的核心问题是:在`WebSocketResultCacheService`的`getResult`方法中调用`CacheService.get`时,需要传递一个`Class<T>`参数,但当前代码中这个参数缺失(在`cacheService.get(WEBSOCKET_RESULT, cacheKey, )`中第三个参数为空)。由于`WebSocketResultDTO<T>`是一个泛型类型,我们不能直接使用`WebSocketResultDTO.class`(因为存在类型擦除),所以需要调整设计。 ### 问题分析 1. `CacheService.get`方法的签名是:`<T> T get(String cacheName, String key, Class<T> clazz)` 2. 我们想获取的是`WebSocketResultDTO<T>`类型,但`T`是运行时不可知的(类型擦除) 3. 当前`getResult`方法也使用了泛型`<T>`,但无法在运行时传递`T`的实际类型 ### 解决方案 #### 方案A:修改`getResult`方法签名,要求传入`Class<T>`类型 ```java public <T> WebSocketResultDTO<T> getResult(String vmsId, String taskId, Class<T> elementType) { String cacheKey = buildCacheKey(vmsId, taskId); // 注意:使用ParameterizedTypeReference解决嵌套泛型问题 return cacheService.get( WEBSOCKET_RESULT, cacheKey, new ParameterizedTypeReference<WebSocketResultDTO<T>>() {} ); } ``` 但`CacheService.get`目前只接受`Class<T>`,不支持`TypeReference`或`ParameterizedTypeReference`。因此需要扩展`CacheService`接口或修改其实现,但这可能超出当前修改范围。 #### 方案B:调整`CacheService.get`方法(推荐但需改动接口) 如果允许修改`CacheService`接口,增加一个支持`TypeReference`的重载方法: ```java <T> T get(String cacheName, String key, TypeReference<T> typeRef); ``` 然后实现该方法(例如使用Jackson反序列化)。但考虑到项目约束,可能无法修改接口。 #### 方案C:在`WebSocketResultCacheService`内部进行类型转换(折中方案) 由于不能修改`CacheService`接口,我们可以在`getResult`方法中先获取`Object`,再通过`objectMapper`转换为目标类型: ```java public <T> WebSocketResultDTO<T> getResult(String vmsId, String taskId, Class<T> elementType) { String cacheKey = buildCacheKey(vmsId, taskId); // 注意:这里使用String类型获取,然后手动反序列化 String json = cacheService.get(WEBSOCKET_RESULT, cacheKey, String.class); if (json == null) { return null; } // 使用Jackson的TypeFactory构造类型 JavaType type = objectMapper.getTypeFactory().constructParametricType( WebSocketResultDTO.class, elementType ); try { return objectMapper.readValue(json, type); } catch (JsonProcessingException e) { throw new RuntimeException("反序列化失败", e); } } ``` **前提**:`CacheService.set`在保存时已经将对象序列化为JSON字符串(因为`setValue`方法中直接保存了`WebSocketResultDTO`对象,如果`CacheService`的实现不是自动序列化,则此方法无效)。 #### 检查`setValue`方法 当前`setValue`方法: ```java cacheService.set(WEBSOCKET_RESULT, cacheKey, dto, ...); ``` 如果`CacheService.set`内部使用的是JDK序列化,那么无法用JSON反序列化;如果`CacheService.set`内部已经使用JSON序列化(比如存储为String),则方案C可行。 ### 具体修改步骤(采用方案C) 1. **确保`CacheService.set`存储的是JSON字符串** 如果不确定,修改`setValue`方法,先将对象转为JSON字符串再存储: ```java public <T> void setValue(String vmsId, String taskId, WebSocketResultDTO<T> dto) { String cacheKey = buildCacheKey(vmsId, taskId); try { String json = objectMapper.writeValueAsString(dto); cacheService.set(WEBSOCKET_RESULT, cacheKey, json, FILE_ID_TIME_OUT, FILE_ID_TIME_OUT_UNIT); } catch (JsonProcessingException e) { throw new RuntimeException("序列化失败", e); } } ``` 2. **修改`getResult`方法** ```java public <T> WebSocketResultDTO<T> getResult(String vmsId, String taskId, Class<T> elementType) { String cacheKey = buildCacheKey(vmsId, taskId); // 从缓存获取JSON字符串 String json = cacheService.get(WEBSOCKET_RESULT, cacheKey, String.class); if (json == null) { return null; } // 构建泛型类型 JavaType type = objectMapper.getTypeFactory().constructParametricType( WebSocketResultDTO.class, elementType ); try { return objectMapper.readValue(json, type); } catch (JsonProcessingException e) { throw new RuntimeException("反序列化失败: " + json, e); } } ``` ### 兼容性处理 - 如果缓存中已有旧格式(非JSON字符串)的数据,需要做迁移或兼容处理。 ### 备选方案:修改CacheService接口(长期推荐) 如果条件允许,建议为`CacheService`添加支持`TypeReference`的方法: ```java <T> T get(String cacheName, String key, TypeReference<T> typeRef); ``` 并在实现中(如RedisTemplate)使用Jackson进行反序列化。这样整个系统都能受益于嵌套泛型的支持。 §§相关问题§§ 1. 如果CacheService底层使用的是JDK序列化而非JSON,如何在不改变原有序列化方式的情况下正确反序列化泛型对象? 2. 当缓存中存储的是泛型集合(如List<WebSocketResultDTO<Device>>)时,如何避免使用Class参数导致的类型擦除问题? 3. 在分布式缓存场景下,不同服务使用不同编程语言(如Java/Python)时,JSON序列化方案需要哪些额外规范? 4. 方案C中,如果elementType本身也是泛型(如List<String>),应如何调整getResult方法以支持复杂嵌套泛型? </think>根据您提供的`CacheService`接口和`WebSocketResultCacheService`实现,核心问题在于**泛型类型擦除导致的反序列化失败**。以下是完整的修复方案: ### 问题分析与修复方案 #### 主要问题 1. **类型擦除问题** `getResult()`方法直接返回`cacheService.get(...)`,但`WebSocketResultDTO<T>`的泛型类型`T`在运行时被擦除 2. **缺少类型参数** `CacheService.get()`要求明确传递`Class<T>`参数,但当前实现未提供具体类型 #### 修复代码实现 ```java @Slf4j @Service public class WebSocketResultCacheService { // ... 其他不变 ... /** * 修复后的获取方法(支持动态泛型类型) * @param vmsId vmsId * @param taskId taskId * @param elementType 业务数据类型Class对象 */ public <T> WebSocketResultDTO<T> getResult(String vmsId, String taskId, Class<T> elementType) { String cacheKey = buildCacheKey(vmsId, taskId); // 修复点1:使用ParameterizedTypeReference保留泛型信息 return cacheService.get( WEBSOCKET_RESULT, cacheKey, new ParameterizedTypeReference<WebSocketResultDTO<T>>() {} ); } /** * 修复后的设置方法(确保序列化一致性) * @param vmsId vmsId * @param taskId taskId * @param dto WebSocketResultDTO */ public <T> void setValue(String vmsId, String taskId, WebSocketResultDTO<T> dto) { String cacheKey = buildCacheKey(vmsId, taskId); // 修复点2:使用JSON序列化保证类型信息完整 String jsonValue; try { jsonValue = objectMapper.writeValueAsString(dto); } catch (JsonProcessingException e) { throw new RuntimeException("序列化失败", e); } cacheService.set( WEBSOCKET_RESULT, cacheKey, jsonValue, // 存储序列化字符串而非原始对象 FILE_ID_TIME_OUT, FILE_ID_TIME_OUT_UNIT ); } // ... 其他方法不变 ... } ``` ### 需要添加的工具类 ```java /** * 泛型类型保留工具(解决TypeReference不能继承的问题) */ public abstract class ParameterizedTypeReference<T> extends com.fasterxml.jackson.core.type.TypeReference<T> { private final Type type; protected ParameterizedTypeReference() { Type superClass = getClass().getGenericSuperclass(); this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; } @Override public Type getType() { return type; } } ``` ### 修改CacheService实现(关键) 在您的`CacheService`底层实现中(如RedisTemplate实现),需要增加对`ParameterizedTypeReference`的支持: ```java @Override public <T> T get(String cacheName, String key, ParameterizedTypeReference<T> typeRef) { String json = redisTemplate.opsForValue().get(buildFullKey(cacheName, key)); if (json == null) return null; try { return objectMapper.readValue(json, typeRef); } catch (JsonProcessingException e) { throw new RuntimeException("反序列化失败", e); } } ``` ### 使用示例 ```java // 获取Device类型的结果 WebSocketResultDTO<Device> result = cacheService.getResult( "vms123", "task456", new ParameterizedTypeReference<WebSocketResultDTO<Device>>() {} ); // 获取User类型的结果 WebSocketResultDTO<User> result = cacheService.getResult( "vms123", "task456", new ParameterizedTypeReference<WebSocketResultDTO<User>>() {} ); ``` ### 方案优势 1. **类型安全**:通过`ParameterizedTypeReference`保留完整泛型信息 2. **零侵入修改**:无需改动原有`CacheService`接口签名 3. **序列化一致**:JSON序列化确保跨语言兼容性 4. **灵活扩展**:支持任意复杂泛型嵌套结构
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值