欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot中熱點(diǎn)KEY緩存優(yōu)化的2種主流策略

 更新時間:2025年04月24日 09:01:40   作者:風(fēng)象南  
所謂熱點(diǎn)KEY,是指在緩存或數(shù)據(jù)庫中被頻繁訪問的少量鍵值,這些鍵往往承載了系統(tǒng)中大部分的訪問流量,本文將分享SpringBoot中三種主流的熱點(diǎn)KEY緩存優(yōu)化策略,大家可以根據(jù)需求進(jìn)行選擇

所謂熱點(diǎn)KEY,是指在緩存或數(shù)據(jù)庫中被頻繁訪問的少量鍵值,這些鍵往往承載了系統(tǒng)中大部分的訪問流量。

根據(jù)二八原則,通常20%的數(shù)據(jù)承擔(dān)了80%的訪問量,甚至在某些極端情況下,單個KEY可能會吸引系統(tǒng)超過50%的流量。

當(dāng)這些熱點(diǎn)KEY沒有得到合理處理時,可能導(dǎo)致:

  • 緩存節(jié)點(diǎn)CPU使用率飆升
  • 網(wǎng)絡(luò)帶寬爭用
  • 緩存服務(wù)響應(yīng)延遲增加
  • 緩存穿透導(dǎo)致數(shù)據(jù)庫壓力驟增
  • 在極端情況下,甚至引發(fā)系統(tǒng)雪崩

本文將深入探討SpringBoot中三種主流的熱點(diǎn)KEY緩存優(yōu)化策略,提升系統(tǒng)在面對熱點(diǎn)KEY時的性能表現(xiàn)。

1. 分級緩存策略

1.1 原理解析

分級緩存策略采用多層次的緩存架構(gòu),通常包括本地緩存(L1)和分布式緩存(L2)。當(dāng)訪問熱點(diǎn)KEY時,系統(tǒng)首先查詢本地內(nèi)存緩存,避免網(wǎng)絡(luò)開銷;僅當(dāng)本地緩存未命中時,才請求分布式緩存。

開源實(shí)現(xiàn)有JetCache、J2Cache

這種策略能有效降低熱點(diǎn)KEY對分布式緩存的訪問壓力,同時大幅提升熱點(diǎn)數(shù)據(jù)的訪問速度。

分級緩存的核心工作流程:

  • 請求首先訪問本地緩存(如Caffeine)
  • 本地緩存命中直接返回數(shù)據(jù)(納秒級)
  • 本地緩存未命中,請求分布式緩存(如Redis)
  • 分布式緩存命中,返回數(shù)據(jù)并回填本地緩存
  • 分布式緩存未命中,查詢數(shù)據(jù)源并同時更新本地和分布式緩存

1.2 實(shí)現(xiàn)方式

步驟1:添加相關(guān)依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

步驟2:配置分級緩存管理器

@Configuration
@EnableCaching
public class LayeredCacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        LayeredCacheManager cacheManager = new LayeredCacheManager(
                createLocalCacheManager(), 
                createRedisCacheManager(redisConnectionFactory)
        );
        return cacheManager;
    }
    
    private CacheManager createLocalCacheManager() {
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        
        // 本地緩存配置 - 為熱點(diǎn)KEY特別優(yōu)化
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(100)                  // 初始大小
                .maximumSize(1000)                     // 最大緩存對象數(shù)
                .expireAfterWrite(1, TimeUnit.MINUTES) // 寫入后1分鐘過期
                .recordStats());                       // 開啟統(tǒng)計
        
        return caffeineCacheManager;
    }
    
    private CacheManager createRedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))      // Redis緩存10分鐘過期
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

步驟3:實(shí)現(xiàn)自定義分級緩存管理器

public class LayeredCacheManager implements CacheManager {
    
    private final CacheManager localCacheManager;  // 本地緩存(L1)
    private final CacheManager remoteCacheManager; // 分布式緩存(L2)
    private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>();
    
    public LayeredCacheManager(CacheManager localCacheManager, CacheManager remoteCacheManager) {
        this.localCacheManager = localCacheManager;
        this.remoteCacheManager = remoteCacheManager;
    }
    
    @Override
    public Cache getCache(String name) {
        return cacheMap.computeIfAbsent(name, this::createLayeredCache);
    }
    
    @Override
    public Collection<String> getCacheNames() {
        Set<String> names = new LinkedHashSet<>();
        names.addAll(localCacheManager.getCacheNames());
        names.addAll(remoteCacheManager.getCacheNames());
        return names;
    }
    
    private Cache createLayeredCache(String name) {
        Cache localCache = localCacheManager.getCache(name);
        Cache remoteCache = remoteCacheManager.getCache(name);
        return new LayeredCache(name, localCache, remoteCache);
    }
    
    // 分級緩存實(shí)現(xiàn)
    static class LayeredCache implements Cache {
        private final String name;
        private final Cache localCache;
        private final Cache remoteCache;
        
        public LayeredCache(String name, Cache localCache, Cache remoteCache) {
            this.name = name;
            this.localCache = localCache;
            this.remoteCache = remoteCache;
        }
        
        @Override
        public String getName() {
            return name;
        }
        
        @Override
        public Object getNativeCache() {
            return this;
        }
        
        @Override
        public ValueWrapper get(Object key) {
            // 先查本地緩存
            ValueWrapper localValue = localCache.get(key);
            if (localValue != null) {
                return localValue;
            }
            
            // 本地未命中,查遠(yuǎn)程緩存
            ValueWrapper remoteValue = remoteCache.get(key);
            if (remoteValue != null) {
                // 回填本地緩存
                localCache.put(key, remoteValue.get());
                return remoteValue;
            }
            
            return null;
        }
        
        @Override
        public <T> T get(Object key, Class<T> type) {
            // 先查本地緩存
            T localValue = localCache.get(key, type);
            if (localValue != null) {
                return localValue;
            }
            
            // 本地未命中,查遠(yuǎn)程緩存
            T remoteValue = remoteCache.get(key, type);
            if (remoteValue != null) {
                // 回填本地緩存
                localCache.put(key, remoteValue);
                return remoteValue;
            }
            
            return null;
        }
        
        @Override
        public <T> T get(Object key, Callable<T> valueLoader) {
            // 先查本地緩存
            ValueWrapper localValue = localCache.get(key);
            if (localValue != null) {
                return (T) localValue.get();
            }
            
            // 本地未命中,查遠(yuǎn)程緩存
            ValueWrapper remoteValue = remoteCache.get(key);
            if (remoteValue != null) {
                // 回填本地緩存
                T value = (T) remoteValue.get();
                localCache.put(key, value);
                return value;
            }
            
            // 遠(yuǎn)程也未命中,調(diào)用值加載器
            try {
                T value = valueLoader.call();
                if (value != null) {
                    // 同時更新本地和遠(yuǎn)程緩存
                    put(key, value);
                }
                return value;
            } catch (Exception e) {
                throw new ValueRetrievalException(key, valueLoader, e);
            }
        }
        
        @Override
        public void put(Object key, Object value) {
            localCache.put(key, value);
            remoteCache.put(key, value);
        }
        
        @Override
        public void evict(Object key) {
            localCache.evict(key);
            remoteCache.evict(key);
        }
        
        @Override
        public void clear() {
            localCache.clear();
            remoteCache.clear();
        }
    }
}

步驟4:在服務(wù)中使用分級緩存

@Service
public class ProductService {
    
    private final ProductRepository productRepository;
    
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    
    // 使用自定義緩存處理熱點(diǎn)商品數(shù)據(jù)
    @Cacheable(value = "products", key = "#id", cacheManager = "cacheManager")
    public Product getProductById(Long id) {
        // 模擬數(shù)據(jù)庫訪問延遲
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return productRepository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException("Product not found: " + id));
    }
    
    // 處理熱門商品列表
    @Cacheable(value = "hotProducts", key = "'top' + #limit", cacheManager = "cacheManager")
    public List<Product> getHotProducts(int limit) {
        // 復(fù)雜查詢獲取熱門商品
        return productRepository.findTopSellingProducts(limit);
    }
    
    // 更新商品信息 - 同時更新緩存
    @CachePut(value = "products", key = "#product.id", cacheManager = "cacheManager")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
    
    // 刪除商品 - 同時刪除緩存
    @CacheEvict(value = "products", key = "#id", cacheManager = "cacheManager")
    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
}

1.3 優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)

  • 顯著降低熱點(diǎn)KEY的訪問延遲,本地緩存訪問速度可達(dá)納秒級
  • 大幅減輕分布式緩存的負(fù)載壓力,提高系統(tǒng)整體吞吐量
  • 減少網(wǎng)絡(luò)IO開銷,節(jié)約帶寬資源
  • 即使分布式緩存短暫不可用,本地緩存仍可提供服務(wù),增強(qiáng)系統(tǒng)彈性

缺點(diǎn)

  • 增加了系統(tǒng)復(fù)雜度,需管理兩層緩存
  • 存在數(shù)據(jù)一致性挑戰(zhàn),不同節(jié)點(diǎn)的本地緩存可能不同步
  • 本地緩存占用應(yīng)用服務(wù)器內(nèi)存資源
  • 適合讀多寫少的場景,寫入頻繁場景效果有限

適用場景

  • 高頻訪問且相對穩(wěn)定的熱點(diǎn)數(shù)據(jù)(如商品詳情、用戶配置)
  • 讀多寫少的業(yè)務(wù)場景
  • 對訪問延遲敏感的關(guān)鍵業(yè)務(wù)
  • 分布式緩存面臨高負(fù)載的系統(tǒng)

2. 緩存分片策略

2.1 原理解析

緩存分片策略針對單個熱點(diǎn)KEY可能導(dǎo)致的單點(diǎn)壓力問題,通過將一個熱點(diǎn)KEY拆分為多個物理子KEY,將訪問負(fù)載均勻分散到多個緩存節(jié)點(diǎn)或?qū)嵗?。這種策略在不改變業(yè)務(wù)邏輯的前提下,有效提升了系統(tǒng)處理熱點(diǎn)KEY的能力。

其核心原理是:

  • 將一個邏輯上的熱點(diǎn)KEY映射為多個物理子KEY
  • 訪問時,隨機(jī)或按某種規(guī)則選擇一個子KEY進(jìn)行操作
  • 寫入時,同步更新所有子KEY,保證數(shù)據(jù)一致性
  • 通過分散訪問壓力,避免單個緩存節(jié)點(diǎn)的性能瓶頸

2.2 實(shí)現(xiàn)方式

步驟1:創(chuàng)建緩存分片管理器

@Component
public class ShardedCacheManager {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private final Random random = new Random();
    
    // 熱點(diǎn)KEY分片數(shù)量
    private static final int DEFAULT_SHARDS = 10;
    
    // 分片KEY的有效期略有差異,避免同時過期
    private static final int BASE_TTL_MINUTES = 30;
    private static final int TTL_VARIATION_MINUTES = 10;
    
    public ShardedCacheManager(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 獲取分片緩存的值
     */
    public <T> T getValue(String key, Class<T> type) {
        // 隨機(jī)選擇一個分片
        String shardKey = generateShardKey(key, random.nextInt(DEFAULT_SHARDS));
        return (T) redisTemplate.opsForValue().get(shardKey);
    }
    
    /**
     * 設(shè)置分片緩存的值
     */
    public void setValue(String key, Object value) {
        // 寫入所有分片
        for (int i = 0; i < DEFAULT_SHARDS; i++) {
            String shardKey = generateShardKey(key, i);
            
            // 計算略有差異的TTL,避免同時過期
            int ttlMinutes = BASE_TTL_MINUTES + random.nextInt(TTL_VARIATION_MINUTES);
            
            redisTemplate.opsForValue().set(
                    shardKey, 
                    value, 
                    ttlMinutes, 
                    TimeUnit.MINUTES
            );
        }
    }
    
    /**
     * 刪除分片緩存
     */
    public void deleteValue(String key) {
        // 刪除所有分片
        List<String> keys = new ArrayList<>(DEFAULT_SHARDS);
        for (int i = 0; i < DEFAULT_SHARDS; i++) {
            keys.add(generateShardKey(key, i));
        }
        
        redisTemplate.delete(keys);
    }
    
    /**
     * 生成分片KEY
     */
    private String generateShardKey(String key, int shardIndex) {
        return String.format("%s:%d", key, shardIndex);
    }
}

步驟2:創(chuàng)建熱點(diǎn)KEY識別和處理組件

@Component
public class HotKeyDetector {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private final ShardedCacheManager shardedCacheManager;
    
    // 熱點(diǎn)KEY計數(shù)器的Hash名稱
    private static final String HOT_KEY_COUNTER = "hotkey:counter";
    
    // 熱點(diǎn)判定閾值 - 每分鐘訪問次數(shù)
    private static final int HOT_KEY_THRESHOLD = 1000;
    
    // 熱點(diǎn)KEY記錄
    private final Set<String> detectedHotKeys = ConcurrentHashMap.newKeySet();
    
    public HotKeyDetector(RedisTemplate<String, Object> redisTemplate,
                          ShardedCacheManager shardedCacheManager) {
        this.redisTemplate = redisTemplate;
        this.shardedCacheManager = shardedCacheManager;
        
        // 啟動定時任務(wù),定期識別熱點(diǎn)KEY
        scheduleHotKeyDetection();
    }
    
    /**
     * 記錄KEY的訪問次數(shù)
     */
    public void recordKeyAccess(String key) {
        redisTemplate.opsForHash().increment(HOT_KEY_COUNTER, key, 1);
    }
    
    /**
     * 檢查KEY是否是熱點(diǎn)KEY
     */
    public boolean isHotKey(String key) {
        return detectedHotKeys.contains(key);
    }
    
    /**
     * 使用合適的緩存策略獲取值
     */
    public <T> T getValue(String key, Class<T> type, Supplier<T> dataLoader) {
        if (isHotKey(key)) {
            // 使用分片策略處理熱點(diǎn)KEY
            T value = shardedCacheManager.getValue(key, type);
            if (value != null) {
                return value;
            }
            
            // 分片中沒有找到,從數(shù)據(jù)源加載并更新分片
            value = dataLoader.get();
            if (value != null) {
                shardedCacheManager.setValue(key, value);
            }
            return value;
        } else {
            // 對于非熱點(diǎn)KEY,使用常規(guī)方式處理
            T value = (T) redisTemplate.opsForValue().get(key);
            if (value != null) {
                return value;
            }
            
            // 緩存未命中,記錄訪問并從數(shù)據(jù)源加載
            recordKeyAccess(key);
            value = dataLoader.get();
            if (value != null) {
                redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES);
            }
            return value;
        }
    }
    
    /**
     * 定期識別熱點(diǎn)KEY的任務(wù)
     */
    private void scheduleHotKeyDetection() {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        
        executor.scheduleAtFixedRate(() -> {
            try {
                // 獲取所有KEY的訪問計數(shù)
                Map<Object, Object> counts = redisTemplate.opsForHash().entries(HOT_KEY_COUNTER);
                
                // 清空之前識別的熱點(diǎn)KEY
                Set<String> newHotKeys = new HashSet<>();
                
                // 識別新的熱點(diǎn)KEY
                for (Map.Entry<Object, Object> entry : counts.entrySet()) {
                    String key = (String) entry.getKey();
                    int count = ((Number) entry.getValue()).intValue();
                    
                    if (count > HOT_KEY_THRESHOLD) {
                        newHotKeys.add(key);
                        
                        // 對新發(fā)現(xiàn)的熱點(diǎn)KEY,預(yù)熱分片緩存
                        if (!detectedHotKeys.contains(key)) {
                            preloadHotKeyToShards(key);
                        }
                    }
                }
                
                // 更新熱點(diǎn)KEY集合
                detectedHotKeys.clear();
                detectedHotKeys.addAll(newHotKeys);
                
                // 清除計數(shù)器,開始新一輪計數(shù)
                redisTemplate.delete(HOT_KEY_COUNTER);
                
            } catch (Exception e) {
                // 異常處理
                e.printStackTrace();
            }
        }, 1, 1, TimeUnit.MINUTES);
    }
    
    /**
     * 預(yù)熱熱點(diǎn)KEY到分片緩存
     */
    private void preloadHotKeyToShards(String key) {
        // 獲取原始緩存中的值
        Object value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            // 將值復(fù)制到所有分片
            shardedCacheManager.setValue(key, value);
        }
    }
}

步驟3:在服務(wù)中集成熱點(diǎn)KEY處理

@Service
public class EnhancedProductService {
    
    private final ProductRepository productRepository;
    private final HotKeyDetector hotKeyDetector;
    
    public EnhancedProductService(ProductRepository productRepository, 
                                 HotKeyDetector hotKeyDetector) {
        this.productRepository = productRepository;
        this.hotKeyDetector = hotKeyDetector;
    }
    
    /**
     * 獲取商品信息,自動處理熱點(diǎn)KEY
     */
    public Product getProductById(Long id) {
        String cacheKey = "product:" + id;
        
        return hotKeyDetector.getValue(cacheKey, Product.class, () -> {
            // 從數(shù)據(jù)庫加載產(chǎn)品信息
            return productRepository.findById(id)
                    .orElseThrow(() -> new ProductNotFoundException("Product not found: " + id));
        });
    }
    
    /**
     * 獲取熱門商品列表,自動處理熱點(diǎn)KEY
     */
    public List<Product> getHotProducts(int limit) {
        String cacheKey = "products:hot:" + limit;
        
        return hotKeyDetector.getValue(cacheKey, List.class, () -> {
            // 從數(shù)據(jù)庫加載熱門商品
            return productRepository.findTopSellingProducts(limit);
        });
    }
    
    /**
     * 更新商品信息,同時處理緩存
     */
    public Product updateProduct(Product product) {
        Product savedProduct = productRepository.save(product);
        
        // 清除所有相關(guān)緩存
        String cacheKey = "product:" + product.getId();
        if (hotKeyDetector.isHotKey(cacheKey)) {
            // 如果是熱點(diǎn)KEY,清除分片緩存
            hotKeyDetector.getShardedCacheManager().deleteValue(cacheKey);
        } else {
            // 常規(guī)緩存清除
            redisTemplate.delete(cacheKey);
        }
        
        return savedProduct;
    }
}

2.3 優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)

  • 有效分散單個熱點(diǎn)KEY的訪問壓力
  • 不依賴于特定的緩存架構(gòu),可適用于多種緩存系統(tǒng)
  • 對客戶端透明,無需修改調(diào)用方代碼
  • 可動態(tài)識別和調(diào)整熱點(diǎn)KEY的處理策略
  • 通過錯峰過期時間,避免緩存雪崩問題

缺點(diǎn)

  • 增加寫入開銷,需同步更新多個緩存分片
  • 實(shí)現(xiàn)復(fù)雜度較高,需維護(hù)熱點(diǎn)KEY檢測和分片邏輯
  • 額外的內(nèi)存占用(一個值存儲多份)
  • 可能引入短暫的數(shù)據(jù)不一致窗口

適用場景

  • 特定KEY訪問頻率遠(yuǎn)高于其他KEY的場景
  • 讀多寫少的數(shù)據(jù)(商品詳情、活動信息等)
  • 大型促銷活動、爆款商品等可預(yù)見的流量突增場景
  • Redis集群面臨單個KEY訪問熱點(diǎn)問題的系統(tǒng)

兩種策略對比

特性分級緩存策略緩存分片策略
主要解決問題熱點(diǎn)KEY訪問延遲熱點(diǎn)KEY單點(diǎn)壓力
實(shí)現(xiàn)復(fù)雜度中等
額外存儲開銷中等
寫入性能影響中等
一致性保障最終一致最終一致
對原有代碼改動中等
適用熱點(diǎn)類型通用熱點(diǎn)超級熱點(diǎn)

總結(jié)

在實(shí)際應(yīng)用中,我們可以根據(jù)業(yè)務(wù)特點(diǎn)和系統(tǒng)架構(gòu)選擇合適的策略,甚至將多種策略組合使用,構(gòu)建更加健壯的緩存體系。

無論選擇哪種策略,都應(yīng)當(dāng)結(jié)合監(jiān)控、預(yù)熱、降級等最佳實(shí)踐,才能真正發(fā)揮緩存的價值,保障系統(tǒng)在面對熱點(diǎn)KEY時的性能和穩(wěn)定性。

最后,緩存優(yōu)化是一個持續(xù)改進(jìn)的過程,隨著業(yè)務(wù)發(fā)展和流量變化,需要不斷調(diào)整和優(yōu)化緩存策略,才能確保系統(tǒng)始終保持高性能和高可用性。

以上就是SpringBoot中熱點(diǎn)KEY緩存優(yōu)化的2種主流策略的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot熱點(diǎn)KEY緩存優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot的ConfigurationProperties或Value注解無效問題及解決

    SpringBoot的ConfigurationProperties或Value注解無效問題及解決

    在SpringBoot項(xiàng)目開發(fā)中,全局靜態(tài)配置類讀取application.yml或application.properties文件時,可能會遇到配置值始終為null的問題,這通常是因?yàn)樵趧?chuàng)建靜態(tài)屬性后,IDE自動生成的Get/Set方法包含了static關(guān)鍵字
    2024-11-11
  • Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

    Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

    ReentrantLock 是常用的鎖,相對于Synchronized ,lock鎖更人性化,閱讀性更強(qiáng),文中將會詳細(xì)的說明,請君往下閱讀
    2021-09-09
  • Java中遍歷ConcurrentHashMap的四種方式詳解

    Java中遍歷ConcurrentHashMap的四種方式詳解

    這篇文章主要介紹了Java中遍歷ConcurrentHashMap的四種方式詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • java商城項(xiàng)目實(shí)戰(zhàn)之購物車功能實(shí)現(xiàn)

    java商城項(xiàng)目實(shí)戰(zhàn)之購物車功能實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了java商城項(xiàng)目實(shí)戰(zhàn)之購物車功能實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • java在linux系統(tǒng)下開機(jī)啟動無法使用sudo命令的原因及解決辦法

    java在linux系統(tǒng)下開機(jī)啟動無法使用sudo命令的原因及解決辦法

    每次開機(jī)自動啟動的java進(jìn)程,頁面上的關(guān)機(jī)按鈕都無法實(shí)現(xiàn)關(guān)機(jī)功能,但是此時如果以chb賬號通過ssh登錄該服務(wù)器,手動殺掉tomcat進(jìn)程,然后再重新啟動tomcat,頁面上的關(guān)機(jī)按鈕就有效了
    2013-08-08
  • Springmvc如何返回xml及json格式數(shù)據(jù)

    Springmvc如何返回xml及json格式數(shù)據(jù)

    這篇文章主要介紹了Springmvc如何返回xml及json格式數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • Java中RedisUtils工具類的使用

    Java中RedisUtils工具類的使用

    本文主要介紹了Java中RedisUtils工具類的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java多線程中的Exchanger應(yīng)用簡析

    Java多線程中的Exchanger應(yīng)用簡析

    這篇文章主要介紹了Java多線程中的Exchanger應(yīng)用簡析,Exchanger提供了一個同步點(diǎn)exchange方法,兩個線程調(diào)用exchange方法時,無論調(diào)用時間先后,兩個線程會互相等到線程到達(dá)exchange方法調(diào)用點(diǎn),此時兩個線程可以交換數(shù)據(jù),將本線程產(chǎn)出數(shù)據(jù)傳遞給對方,需要的朋友可以參考下
    2023-12-12
  • Flutter實(shí)現(xiàn)容器組件、圖片組件 的代碼

    Flutter實(shí)現(xiàn)容器組件、圖片組件 的代碼

    容器組件(Container)可以理解為在Android中的RelativeLayout或LinearLayout等,在其中你可以放置你想布局的元素控件,從而形成最終你想要的頁面布局。這篇文章主要介紹了Flutter實(shí)現(xiàn)容器組件、圖片組件 的代碼,需要的朋友可以參考下
    2019-07-07
  • JAVASE系統(tǒng)實(shí)現(xiàn)抽卡功能

    JAVASE系統(tǒng)實(shí)現(xiàn)抽卡功能

    這篇文章主要為大家詳細(xì)介紹了JAVASE系統(tǒng)實(shí)現(xiàn)抽卡功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11

最新評論