[java并发] java高并发系列 - 第29天:高并发中常见的限流方式

java并发系列:高并发中常见的限流方式 环境:jdk1.8。 本文内容 介绍常见的限流算法 通过控制最大并发数来进行限流 通过漏桶算法来进行限流 通过令牌桶算法来进行限流 限流工具类RateLimiter 常见的限流的场景 秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理 国庆期间,一般的旅游景点人口太多,采用排队方式做限流处理 医院看病通过发放排队号的方式来做限流处理。 常见的限流算法 通过控制最大并发数来进行限流 使用漏桶算法来进行限流 使用令牌桶算法来进行限流 通过控制最大并发数来进行限流 以秒杀业务为例,10个iphon. 阅读详情

原文链接:查看原文

感谢公众号“ 路人甲Java”的分享,如有冒犯,请联系删除,快去关注他吧
在这里插入图片描述


本文内容:

  1. 介绍常见的限流算法
  2. 通过控制最大并发数来进行限流
  3. 通过漏桶算法来进行限流
  4. 通过令牌算法来进行限流
  5. 限流工具类RateLimiter

常见的限流的场景

  1. 秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理
  2. 国庆期间,一般的旅游景点人口见多,采用排队方式做限流处理
  3. 医院看病通过发放排队号来做限流处理

常见的限流算法

  1. 通过控制最大并发数来进行限流
  2. 使用漏桶算法来进行限流
  3. 使用令牌桶算法来进行限流

通过控制最大并发数来进行限流

以秒杀业务为例,10个iphone,100万人抢购,100万人同时发起请求,最终能够抢到的人也就是前面几个人,后面的基本都没有希望了,那么我们可以通过控制并发数来实现,比如并发数控制在10个,其他的超过并发数的请求全部拒绝,提示“秒杀失败,请稍后重试!”。

并发控制的通俗解释:一大波人去商场购物,必须经过一个门口,门口有个门卫,兜里有指定数量的门禁卡,来的人先去门口那拿取门禁卡,拿到卡的人才可以刷卡进入商场,拿不到的可以继续等待。进入的人出来之后会把卡归还给门卫,门卫可以把归还来的卡继续发给其他排队的顾客使用。

JUC中提供了这样的工具类:Semaphore,实例代码:

package aboutThread.Concurrent.Day29;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Demo1 {
    
    static Semaphore semaphore = new Semaphore(5);
    
    public static void main(String[] args){
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                boolean flag = false;
                try {
                    flag = semaphore.tryAcquire(100, TimeUnit.MICROSECONDS);
                    if(flag){
                        //休眠2秒,模拟下单操作
                        System.out.println(Thread.currentThread() + ", 尝试下单中。。。");
                        TimeUnit.SECONDS.sleep(2);                        
                    }else{
                        System.out.println(Thread.currentThread() + ", 秒杀失败,请稍后重试!");  
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    if(flag){
                        semaphore.release();
                    }
                }
            }).start();
        }
    }

}

运行代码,得到:

Thread[Thread-0,5,main], 尝试下单中。。。
Thread[Thread-2,5,main], 尝试下单中。。。
Thread[Thread-3,5,main], 尝试下单中。。。
Thread[Thread-1,5,main], 尝试下单中。。。
Thread[Thread-5,5,main], 秒杀失败,请稍后重试!
Thread[Thread-8,5,main], 秒杀失败,请稍后重试!
Thread[Thread-7,5,main], 秒杀失败,请稍后重试!
Thread[Thread-9,5,main], 秒杀失败,请稍后重试!
Thread[Thread-4,5,main], 尝试下单中。。。
Thread[Thread-10,5,main], 秒杀失败,请稍后重试!
Thread[Thread-11,5,main], 秒杀失败,请稍后重试!
Thread[Thread-12,5,main], 秒杀失败,请稍后重试!
Thread[Thread-13,5,main], 秒杀失败,请稍后重试!
Thread[Thread-14,5,main], 秒杀失败,请稍后重试!
Thread[Thread-15,5,main], 秒杀失败,请稍后重试!
Thread[Thread-16,5,main], 秒杀失败,请稍后重试!
Thread[Thread-17,5,main], 秒杀失败,请稍后重试!
Thread[Thread-18,5,main], 秒杀失败,请稍后重试!
Thread[Thread-19,5,main], 秒杀失败,请稍后重试!
Thread[Thread-6,5,main], 秒杀失败,请稍后重试!

关于 Semaphore 的使用,请移步:JUC中的Semaphore(信号量)

使用漏桶算法来进行限流

国庆期间比较火爆的景点,人流量巨大,一般入口会有限流的弯道,让游客进去进行排队,排在前面的人,每隔一段时间会放一波进入景区。排队人数超过了指定的限制,后面再来的人会被告知今天游客量已经达到峰值,会被拒绝排队,让其明天或者以后再来,这种玩法采用漏桶限流的方式。

漏桶算法的思路很简单,水(请求)先进到漏桶里,漏桶以一定的速度出水,当水流速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

漏桶算法示意图:
在这里插入图片描述

简陋版的实现如下:

package aboutThread.Concurrent.Day29;

import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Demo2 {

    public static class BucketLimit{
        static AtomicInteger threadNum = new AtomicInteger(1);
        //容量
        private int capacity;
        // 流速
        private int flowRate;
        //流速时间单位
        private TimeUnit flowRateUnit;
        private BlockingQueue<Node> queue;
        // 漏桶流出的任务时间间隔
        private long flowRateNanosTime;

        public BucketLimit(int capacity,int flowRate,TimeUnit flowRatTimeUnit){
            this.capacity = capacity;
            this.flowRate = flowRate;
            this.flowRateUnit = flowRatTimeUnit;
            this.bucketThreadWork();
        }

        //漏桶线程
        public void bucketThreadWork(){
            this.queue = new ArrayBlockingQueue<Node>(capacity);
            //漏桶流出的任务时间间隔(纳秒)
            this.flowRateNanosTime = flowRateUnit.toNanos(1) / flowRate;
            Thread thread = new Thread(this::bucketWork);
            thread.setName("漏桶线程-" + threadNum.getAndIncrement());
            thread.start();
        }

        //漏桶线程开始工作
        public void bucketWork(){
            while(true){
                Node node = this.queue.poll();
                if(Objects.nonNull(node)){
                    //唤醒任务线程
                    LockSupport.unpark(node.thread);
                }
            }
        }

        //返回一个漏桶
        public static BucketLimit build(int capacity,int flowRate,TimeUnit flowRateTimeUnit){
            if(capacity < 0 || flowRate < 0){
                throw new IllegalArgumentException("capacity、flowRate必须大于0");
            }
            return new BucketLimit(capacity, flowRate, flowRateTimeUnit);
        }

        //当前线程加入漏桶,返回false,表示桶已满,true:表示被漏桶限流成功,可以继续处理任务
        public boolean accquire(){
            Thread thread = Thread.currentThread();
            Node node = new Node(thread);
            if(this.queue.offer(node)){
                LockSupport.park();
                return true;
            }
            return false;
        }

        class Node{
            private Thread thread;
            public Node(Thread thread){
                this.thread = thread;
            }
        }
    }
    public static void main(String[] args){
     
        BucketLimit bucketLimit = BucketLimit.build(10, 60, TimeUnit.MINUTES);
        for (int i = 0; i < 15; i++) {
            new Thread(() -> {
                boolean accquire = bucketLimit.accquire();
                System.out.println(System.currentTimeMillis() + "  " + accquire);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
            }).start();
        }

    }
}

运行结果:

1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019086  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true
1596533019085  true

代码中 BucketLimit.build(10,60,TimeUnit.MINUTES); 创建了一个容量为10,流水为60/分钟的漏桶。

代码中用到的技术有:


使用令牌桶算法来进行限流

令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。从原理上看,令牌桶算法和漏桶算法时相反的,一个“进水”,一个是“漏水”。这种算法可以应对突发程度的请求,因此比漏桶算法好。

令牌桶算法示意图:

在这里插入图片描述


限流工具类RateLimiter

Google开源工具包Guava提供了限流工具类RateLimiter,可以非常方便的控制系统每秒吞吐量,实例代码如下:

package com.itsoku.chat29;

import com.google.common.util.concurrent.RateLimiter;

import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * 跟着阿里p7学并发,微信公众号:javacode2018
 */
public class Demo3 {

    public static void main(String[] args) throws InterruptedException {
        RateLimiter rateLimiter = RateLimiter.create(5);//设置QPS为5
        for (int i = 0; i < 10; i++) {
            rateLimiter.acquire();
            System.out.println(System.currentTimeMillis());
        }
        System.out.println("----------");
        //可以随时调整速率,我们将qps调整为10
        rateLimiter.setRate(10);
        for (int i = 0; i < 10; i++) {
            rateLimiter.acquire();
            System.out.println(System.currentTimeMillis());
        }
    }

输出:

1566284028725
1566284028922
1566284029121
1566284029322
1566284029522
1566284029721
1566284029921
1566284030122
1566284030322
1566284030522
----------
1566284030722
1566284030822
1566284030921
1566284031022
1566284031121
1566284031221
1566284031321
1566284031422
1566284031522
1566284031622

代码中==RateLimiter.create(5)创建QPS为5的限流对象,后面又调用rateLimiter.setRate(10);==将速率设为10,输出中分2段,第一段每次输出相隔200毫秒,第二段每次输出相隔100毫秒,可以非常精准的控制系统的QPS。

每秒查询率(QPS,Queries-per-second)是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。

上面介绍的这些,业务中可能会用到,也可以用来应对面试。

削峰+限流:秒杀场景下的高并发写请求解决方案 我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“”,获取更多技术干货!哈喽,大家好!我是小米,一个29岁、活泼积极、热衷分享技术的码农。今天和大家聊一聊应对高并发的写请求这个主题,尤其是在大促、秒杀这种场景下,系统该如何应对突如其来的流量洪峰!每逢秒杀,瞬间涌入的请求量可能直接把你的系统给“砸”了。要如何应对这类场景呢?别着急,今天我就给大家从四个方面详细讲解,教你如何扛住这波流量,稳定服务! 阅读详情

相关推荐

java并发系列 - 第29天:高并发中常见的限流方式

从原理上看,令牌桶算法和漏桶算法是相反的,一个“进水”,一个是“漏水”。并发控制的,通俗解释:一大波人去商场购物,必须经过一个门口,门口有个门卫,兜里面有指定数量的门禁卡,来的人先去门卫那边拿取门禁卡,拿到卡的人才可以刷卡进入商场,拿不到的可以继续等待。以秒杀业务为例,10个iphone,100万人抢购,100万人同时发起请求,最终能够抢到的人也就是前面几个人,后面的基本上都没有希望了,那么我们可以通过控制并发数来实现,比如并发数控制在10个,其他超过并发数的请求全部拒绝,提示:秒杀失败,请稍后重试。

专搞技术的小兔子 236

Java高并发实战(1) - 使用Semaphore对单接口进行限流

一、实战说明 1.1 效果说明 本篇主要讲如何使用Semaphore对单接口进行限流,例如有如下场景 a. A系统的有a接口主要给B系统调用,现在希望对B系统进行限流,例如处理峰值在100,超过100的请求快速失败 b. 接口作为总闸入口,希望限制所有外来访问,例如某个房间只能同时100个玩家在线,只有前面的处理完后面的才能继续请求 c. 其他类型场景,也就是资源固定的情况下需要轮流使用资源的可以采用Semaphore 不适用场景 a. 由于是针对总入口进行限流,所以不能根据IP或者token等进行限流,

TianXinCoord的博客 2302

智能车竞赛独轮组信标灯系统全解析:从硬件选型到实战调试

本文全面解析了智能车竞赛独轮组信标灯系统的核心原理与实战应用。从硬件架构(灯板、灯罩、控制器)的深度剖析,到红外信号(特别是40kHz调制信号)的接收与解码策略,再到车模的灭灯机构设计与路径规划,提供了从硬件选型到软件调试的全链路指南。文章重点分享了实战调试技巧与常见问题排查方法,旨在帮助参赛队伍深入理解系统,提升车模的识别精度与比赛表现。

e4f5g6h的博客 1124

Java中的并发工具类

Java中的并发工具类 在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段。本章会配合一些应用场景来介绍如何使用这些工具类。 等待多线程完成的CountDownLatch public class CountDownLatchTe...

weixin_30666943的博客 102

开源onlyoffice编译完整过坑版

onlyoffice 编译过坑完整版;

sHImart 4551

[RK3568 Android11] 教程之原厂SDK源码适配AIO-3568J板子跑起来

目录 一、前提 二、修改dts设备树 三、适配板子的dts 四、编译系统烧入固件验证 一、前提 因源码是直接使用原厂的SDK,没有使用firefly配套的SDK源码,所以手上这块firefly aio-3568j板子在硬件设计上与原厂使用的板子会有些差异;就是因为这些差异会导致新固件烧入后,debug串口打印许多错误信息,可能的话,系统还会崩溃掉,跑不起来; 主要差异在哪里呢?比如: ● DDR类型和品牌多样性; ...

~未来可期~ 1744

Java高并发系统的限流策略

概要 在大数据量高并发访问时,经常会出现服务或接口面对暴涨的请求而不可用的情况,甚至引发连锁反映导致整个系统崩溃。此时你需要使用的技术手段之一就是限流,当请求达到一定的并发数或速率,就进行等待、排队、降级、拒绝服务等。 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。 缓存 缓存比较好理解,在大型高并发系统中,如果没有缓存数据库将分分钟被爆,系统也会

syc001的专栏 9066

YOLOv8改进策略【注意力机制篇】| CVPRW-2024 分层互补注意力混合层 H-RAMi 针对低质量图像的特征提取模块

许多证据表明层次化网络对图像恢复(IR)任务通常不太有效,因为IR的目标是逐个预测像素值(密集预测),而缩小特征图会丢失重要的像素级信息。然而,层次化结构有降低时间复杂度以及学习语义级和像素级特征表示的优点。为了弥补缺点并利用优点,设计了H - RAMi层。

Limiiiing的博客 378

java高并发系列 - 第29天:高并发中常见的限流方式

这是java高并发系列第29篇。环境:jdk1.8。本文内容介绍常见的限流算法通过控制最大并发数来进行限流通过漏桶算法来进行限流通过令牌桶算法来进行限流限流工具类Rate...

路人甲Java 1154

第170篇 因子图优化入门——图模型在状态估计中的应用

本文系统介绍了从滤波方法到因子图优化的思维转变,重点解析了因子图的原理与应用。首先对比了递推式滤波(卡尔曼滤波等)与全局优化的区别,指出优化方法能利用所有信息且适合处理稀疏结构。然后详细阐述了因子图的二部图结构(变量节点和因子节点)及其将复杂概率分布分解为小因子乘积的优势。文章讲解了因子图优化如何转化为最小二乘问题,并介绍高斯-牛顿法和LM方法等求解技术。最后对比了g2o、Ceres和GTSAM三大主流框架的特点及适用场景,强调了初始化和鲁棒核函数的重要性,为SLAM相关面试提供了实用建议。

郑伟强dev的专栏 374

仿真学习系列(四十二):Sigrity仿真之走线阻抗和耦合检查

在高速电路设计中,走线的阻抗和信号线之间的耦合是影响信号完整性的重要因素。设计中可能会出现多种情况,导致阻抗变化或不匹配,例如线宽的变化、跨层分割等。传统的人工检查方法常常不够高效且易忽视细节。借助仿真软件,可以快速定位问题区域,并进行精确分析,从而有效避免或减少信号反射和串扰带来的影响。

案例文章,高速信号 425

java高并发系列 - 第29天:高并发中常见的限流方式,这是一份用心整理的Java面试总结

本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家![外链图片转存中…(img-3IU2efTd-1711144077072)][外链图片转存中…(img-6esULU9j-1711144077073)][外链图片转存中…(img-pRF2gRWP-1711144077073)][外链图片转存中…(img-1a8nMsWz-1711144077074)]

654

C语言使用开源库libusb进行USB设备操作

https://libusb.info/libusb is a C library that provides generic access to USB devices. It is intended to be used by developers to facilitate the production of applications that communicate with USB hardware. It is portable: Using a single cross-platform AP

m0_60352504的博客 1554

java学习系列-高并发25:高并发中常见的限流方式

常见的限流的场景 秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理 国庆期间,一般的旅游景点人口太多,采用排队方式做限流处理 医院看病通过发放排队号的方式来做限流处理。 常见的限流算法 通过控制最大并发数来进行限流 使用漏桶算法来进行限流 使用令牌桶算法来进行限流 通过控制最大并发数来进行限流 以秒杀业务为例,10个iphone,100万人抢购,100万人同时发起请求,最终能够抢到的人也就是前面几个人,后面的基本上都没有希望了,那么我们可以通过控制并发数来实现,比如并发数控制在10个

星之宇的博客 392

flowable工作流看这一篇就够了(基础篇)

接下来我们构建一个非常简单的请假流程,Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。在Flowable术语中,我们将其称为一个流程定义(process definition)。一个流程定义可以启动多个流程实例(process instance)。流程定义可以看做是重复执行流程的蓝图。在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。

qq_38196449的博客 2万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

访问接口常用限流的java方式_java并发系列:高并发中常见的限流方式

环境:jdk1.8。本文内容介绍常见的限流算法通过控制最大并发数来进行限流通过漏桶算法来进行限流通过令牌桶算法来进行限流限流工具类RateLimiter常见的限流的场景秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理国庆期间,一般的旅游景点人口太多,采用排队方式做限流处理医院看病通过发放排队号的方式来做限流处理。常见的限流算法通过控制最大并发数来进行限流使用漏桶算法来进行限流使用令...

weixin_39747721的博客 247

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Java高并发专题之29、实战:一起来搞懂限流

本文内容 介绍常见的限流算法 通过控制最大并发数来进行限流 通过漏桶算法来进行限流 通过令牌桶算法来进行限流 限流工具类RateLimiter 常见的限流的场景 秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理 国庆期间,一般的旅游景点人口太多,采用排队方式做限流处理 医院看病通过发放排队号的方式来做限流处理。 常见的限流算法 通过控制最大并发数来进行限流 使用漏桶...

优快云_KONGlX的博客 144

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java常见5种限流算法及7种限流方式

最近几年,随着微服务的流行,服务和服务之间的依赖越来越强,调用关系越来越复杂,服务和服务之间的稳定性越来越重要。在遇到突发的请求量激增,恶意的用户访问,亦或请求频率过高给下游服务带来较大压力时,我们常常需要通过缓存、限流、熔断降级、负载均衡等多种方式保证服务的稳定性。其中限流是不可或缺的一环,这篇文章介绍限流相关知识。这篇文章介绍实现限流的几种方式,主要是窗口算法和桶算法,两者各有优势。

MayMatrix 的博客 6965

java高并发系列

获取线程执行结果:https://blog.youkuaiyun.com/likun557/article/details/100070370 java高并发系列目录: 第1天:必须知道的几个概念:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648933019&idx=1&sn=3455877c451de9c61f839...

weixin_43719015的博客 203

29W 字总结阿里 Java 高并发编程:案例 + 源码 + 面试 + 系统架构设计

下半年的跳槽季已经开始,好多同学已经拿到了不错的 Offer,同时还有一些同学对于 Java 高并发编程还缺少一些深入的理解,不过不用慌,今天老师分享的这份 27W 字的阿里巴巴 Java 高并发编程帮你保驾护航!限时分享给咱们有需要的同学,如果你不知道是否需要这份笔记请接着往下看! 这份笔记共计分为了五大版块:基础案例版块、实战案例版块、源码分析版块、面试突击版块、系统架构设计版块! 下面咱们将会分别从这五大版块学习 Java 高并发编程!由于文章篇幅有限,老师已经将这五大版块整合成了一份完整...

m0_67322837的博客 1030

java每日精进 5.29【请求限流】

Class<?解释属性time 和 timeUnit:限流时间窗口,默认 1 秒。count:时间窗口内的最大请求次数,默认 100。message:超限时的错误提示,默认为 TOO_MANY_REQUESTS 消息。keyResolver:键解析器,支持 DefaultRateLimiterKeyResolver(全局)、UserRateLimiterKeyResolver(用户)、ClientIpRateLimiterKeyResolver(IP)等。

weixin_51721783的博客 687

【Java并发请求处理方案】:揭秘高并发场景下的线程池优化与异步处理实战

掌握高效Java并发请求处理方案,提升系统吞吐量与响应速度。适用于高并发Web服务场景,结合线程池优化、异步编程与CompletableFuture实战,有效降低资源消耗。性能提升显著,值得收藏。

LiteTrans的博客 1030

Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!1.JPG (37.82 KB, 下载次数: 0)2018-12-3 09:40 上传2.JPG (28.38 KB, 下载次数: 0)2018-12-3 09:40 上传3.JPG (44.5 KB, 下载次数: 0)2018-12-3 09:40 上传===============课程目录========...

weixin_36284848的博客 177
上一篇: [Spring基础]原来,Spring实现定时器是这样的!
下一篇: [java设计模式]我眼中的抽象工厂模式
Aran_
博客等级 码龄7年 7粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值