Java面试题(JUC)

Java并发编程面试题——JUC专题 1.1AQS是什么?1.2唤醒线程时,AQS为什么从后往前遍历?1.3AQS为什么用双向链表,(为啥不用单向链表)?1.4AQS为什么要有一个虚拟的head节点1.5ReentrantLock的底层实现原理1.6ReentrantLock的公平锁和非公平锁的区别1.7ReentrantReadWriteLock如何实现的读写锁2.1说下你熟悉的阻塞队列?2.2虚假唤醒是什么?3.1线程池的7个参数3.2线程池的状态有什么,如何记录的?3.3线程池常见的拒绝策略3.4线程池执行流程3.5线程池为什么添加空任务 阅读详情

1. CountDownLatch

CountDownLatch:当所有的子线程完成任务,主线程才能结束

  • 使用CountDownLatch:当一个或者多个线程调用await方法时,线程会阻塞,其他线程调用countDown方法会将计数器-1(调用countDown方法的线程不会阻塞);
  • 当计数器变为0时,此时被await方法阻塞的线程才会被唤醒,继续执行
public class CountDownLatchDemo {

    public static void main(String[] args) {
        
        CountDownLatch count = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 结束...");
                count.countDown(); // 每次一个子线程完成 计数-1
            }, String.valueOf(i)).start();
        }
        try {
            count.await(); // 在所有的子线程执行完成之前 主线程必须等待
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 子线程全部执行完...总程序执行结束...");

    }
}

2. CyclicBarrier

CyclicBarrier(int parties, Runnable barrierAction):在给定数量的参与者(线程)全部处于等待状态时启动,并在启动barrier时执行给定的屏障操作。该操作由最后一个进入barrier的线程执行

  • int parties 参与者的数量
  • Runnable barrierAction 线程全部就绪后执行的方法
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(7, () -> {
            System.out.println("线程全部就绪...开始执行...");
        });

        for(int i=1; i<=7; i++){
            int finalI = i;
            new Thread( () -> {
                System.out.println(Thread.currentThread().getName() + "第 " + finalI + "线程就绪");
                try{
                    barrier.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

3. Semaphore

使用信号量Semaphore的目的:用于多个共享资源的互斥使用;并发线程数的控制(可替换Synchronized)

在信号量semaphore上定义两种操作:

  • acquire获取:当一个线程调用acquire操作时,要么通过成功获取信号量(信号量-1);要么一直等待下去,直到有线程释放信号量或者超时

  • release释放:实际上信号量的值+1,然后唤醒等待的线程

    注意:当创建信号量传递的资源数只有1个,相当于使用了加锁synchronized

public class SemaphoreDemo {

    public static void main(String[] args) {
        // 模拟资源类有三个
        Semaphore semaphore = new Semaphore(3);
        // 6个线程去抢占资源,每当一个线抢占到资源占用4秒
        for(int i=1; i<=6; i++){
            new Thread(() -> {
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 抢占资源");
                try {
                    TimeUnit.SECONDS.sleep(4);
                    System.out.println(Thread.currentThread().getName() + " 释放资源");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release(); // 释放资源后需要将资源数+1
                }
            }, String.valueOf(i)).start();
        }

    }
}

4. 阻塞队列BlockingQueue

阻塞:某些情况会挂起线程,一旦条件满足,又会被唤醒

  • 为什么需要阻塞队列:不需要关心什么时候要阻塞线程,什么时候需要唤醒,交给BlockingQueue来完成

  • 阻塞队列是一个队列,Thread1在队列头,Thread2在队列尾

  • 线程1往阻塞队列中添加,线程2移除从空的队列中获取元素的线程被阻塞,直到其他线程在空的队列中增加元素。从满的队列中增加元素的线程被阻塞,直到其他线程在队列中移除一个或者多个

  • BlockingQueue继承Queue,BlockingQueue的实现类有:

    1. ArrayBlockingQueue 数组结构的有界阻塞队列
    2. LinkedBlockingQueue 链表结构的有界阻塞队列
    3. SynchronousQueue 不存储元素的阻塞队列 也就是单个元素的队列

    其中的API有:

  • 当队列满时 再添加元素add报错:IllegalStateException: Queue full

  • 当队列空时 再移除元素remove报错:IllegalStateException: NoSuchElementException

  • 使用offer和poll不抛异常,只是返回true/false/null

  • 而使用put方法,队列满时会一直进行等待 直至有空位置; 使用take方法,队列空时会一直进行等待 直至有元素可以取

  • offer(E e, long timeout, TimeUnit unit) 设置超时时间 不会一直等待下去

  • poll(long timeout, TimeUnit unit) 设置超时时间 不会一直等待下去 时间到没有值则返回null

方法类型抛出异常特殊值阻塞超时
插入add(E e)offer(e)put(e)offer(e, time, unit)
移除remove()poll()take()poll(time,unit)
检查element()peek()不可用不可用

SynchronousQueue:不存储元素的阻塞队列,即单个元素的队列;每一个put操作,都必须要等待一个take操作,否则不能继续添加元素,反之亦然

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    
    public static void main(String[] args) {
        // 默认是非公平锁
        BlockingQueue<String> queue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName()+ "\t put 111");
                queue.put("111");
                System.out.println(Thread.currentThread().getName()+ "\t put 222");
                queue.put("222");
                System.out.println(Thread.currentThread().getName()+ "\t put 333");
                queue.put("333");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "A").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "\t" +  queue.take());

                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "\t" +  queue.take());
            
                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "\t" +  queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "B").start();
    }
}

使用BlockingQueue实现生产者-消费者问题,不需要加锁Lock或者synchronized:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class Resources{

    private volatile boolean flag = true; // 默认开启 生产+消费
    private AtomicInteger atomicInteger = new AtomicInteger();

    BlockingQueue<String> queue = null;
    // 通过构造方法来创建BlockingQueue
    public Resources(BlockingQueue<String> queue) {
        this.queue = queue;
        System.out.println("当前阻塞队列类型为:" + queue.getClass().getName());
    }

    public void Product() throws InterruptedException {
        String data = null;
        boolean returnValue;
        while(flag){
            data = atomicInteger.incrementAndGet() + "";
            returnValue = queue.offer(data, 2, TimeUnit.SECONDS);
            if(returnValue){
                System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "成功");
            }else{
                System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "失败");
            }
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName() + "\t 生产结束!");
    }

    public void Consumer() throws InterruptedException {
        String res = null;
        while(flag){
            res = queue.poll(2, TimeUnit.SECONDS);
            if(res == null || res.equalsIgnoreCase("")){
                flag = false;
                System.out.println(Thread.currentThread().getName()+ "\t 2s钟没等到值 出队暂停");
                return;
            }
            System.out.println(Thread.currentThread().getName() + "\t 出队成功" + res);
        }
    }
    public void stop(){
        this.flag = false;
    }
}

public class ProductConsumer_BlockQueueDemo {

    public static void main(String[] args) {

        Resources resources = new Resources(new ArrayBlockingQueue<>(10));
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 生产线程启动");
            try {
                resources.Product();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 消费线程启动");
            try {
                resources.Consumer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();

        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程停止... 生产消费结束");
        resources.stop();

    }
}

请添加图片描述

5. 死锁

死锁:两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉他们将无法推进下去

代码演示:

import java.util.concurrent.TimeUnit;

class holdLockThread implements Runnable{

    private String lock1;
    private String lock2;

    public holdLockThread(String lock1, String lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }

    @Override
    public void run() {
        synchronized (lock1){
            System.out.println(Thread.currentThread().getName() + "\t 自己持有:" +lock1 + "\t 尝试获得:" + lock2);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2){
                System.out.println(Thread.currentThread().getName() + "\t 自己持有:" +lock2 + "\t 尝试获得:" + lock1);
            }
        }
    }
}

public class DeadLockDemo {

    public static void main(String[] args) {
        String lock1 = "AAA";
        String lock2 = "BBB";

        new Thread( new holdLockThread(lock1, lock2), "ThreadAAA").start();
        new Thread( new holdLockThread(lock2, lock1), "ThreadBBB").start();
   
    }
}

解决:

  • windows下查看java运行程序 类似ps的查看线程的命令 在IDEA的terminal中使用jps-l 定位哪一个线程

  • 14812 com.hz.Interview.DeadLockDemo 线程id 出问题的线程

  • jstack 14812(线程id) 找到该线程的死锁查看
    请添加图片描述
    请添加图片描述

【Java面试题汇总】多线程、JUC、锁篇(2026版) 线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC 阅读详情

相关推荐

Python:同花顺全数据接口

# -*- coding: utf-8 -*- from iFinDPy import * from datetime import datetime import pandas as pd import time as _time import json from threading import Thread,Lock,Semaphore import requests sem = Semaphore(5) # 此变量用于控制最大并发数 dllock = Lock() #此变量用来控制实时行情推

代码过客 4万+

18个JUC常见面试题

ThreadLocal 是一种线程本地变量,可以允许多个线程并发访问同一个变量,但它是基于线程的隔离机制实现的,可以保证变量对每个线程都是独立的。但它只能实现独占锁,不能实现读写分离。Phaser(阶段器)是一种新的线程同步工具,主要用于分阶段的任务,它可以灵活的控制阶段的完成次数,当所有关联的线程都完成当前阶段时,才可以进入下一阶段,可以。CAS 的实现原理是,先读取变量的内存地址中的值,和期望的值进行比较,如果相同,则将新值写入这个内存地址,如果不同,则重试整个操作,直到交换成功为止。

2301_77181435的博客 4017

【项目分享】RailTracker: 火车票务数据采集与分析

从12306使用爬虫爬取火车站及车次信息、火车票价 通过本项目,我们将带领访问者手把手完成火车票数据采集、数据清洗、数据集成、可视化的一整个流程!

zhushatong的博客 3345

java经典面试题JUC并发篇(持续更新)

什么是ABA:在CAS过程中,线程1、线程2分别从内存中拿到了当前值为A,同时线程2把当前值A改为B,随后又把B改回来变为A,此后线程1检查到当前值仍为A而导致执行cas成功,但这个过程却发生了ABA问题,现场资源可能和当初不一样了(线程2把当前值由A->B->A)解决方法:版本号机制,利用版本号标记线程1拿到的‘当前值’的版本,若线程2进行了A->B->A操作,则版本号会改变,那线程1再次拿到的‘当前值’的版本和第一次的肯定是不同的,从而判定cas失败;解释器JIT对热点代码的优化。

欢迎来到烟火气知识小天地!这里记录生活、工作中的 “小麻烦”,从软件故障到生活难题,我会毫无保留分享解决思路与方法,愿成为你排忧解难时的温暖参考,伴你在解决问题路上稳步前行。 5500

[LaTeX] 调整参考文献的格式(References),包括作者名缩写,行距,字体,引用顺序等等

在此记录我对LaTeX中参考文献references的尝试,主要涵盖如题几个方面。

the Blog of 等不到天亮丶等时光 4万+

KEITHLEY 2182A + 6220/6221 系列测试系统软件

6221型与2182A型相结合,是业界最完整的差分电导测量解决方案

业余程序员小徐的博客 2989

Java 面试问题 —— 多线程模块(JUC 模块)

JUC多线程面试题——多线程高阶

baiyang2001的博客 1421

Rocky Linux 9 离线环境Docker部署实战:从依赖打包到服务启动

本文提供了一份详尽的Rocky Linux 9离线环境Docker部署实战指南。针对无法访问外网的生产或内网服务器,文章详细讲解了如何在联网跳板机上下载Docker二进制包及所有系统依赖RPM包,并完成打包。随后,手把手指导在离线服务器上进行依赖安装、二进制文件部署、systemd服务配置、SELinux与防火墙调优,最终实现Docker服务的成功启动与验证。

weixin_28363123的博客 138

学java怎么做笔记?教大家整理java笔记的一些方法,2024年最新juc并发编程面试题

看完美团、字节、腾讯这三家的面试问题,是不是感觉问的特别多,可能咱们又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。开篇有提及我可是足足背下了1000道题目,多少还是有点用的呢,我看了下,上面这些问题大部分都能从我背的题里找到的,所以今天给大家分享一下互联网工程师必备的面试1000题。注意不论是我说的互联网面试1000题,还是后面提及的算法与数据结构、设计模式以及更多的Java学习笔记等,皆可分享给各位朋友互联网工程师必备的面试1000题而且从上面三家来看,算法与数据结构是必备不可少的。

2401_84437530的博客 927

嵌入式工程师必看:BLDC电机驱动电路设计全流程(附MOS管选型指南)

本文为嵌入式工程师提供了BLDC电机驱动电路设计的全流程实战指南。文章深入解析了三相全桥逆变电路的核心原理,并重点阐述了功率MOSFET的选型关键参数,如Vds、Rds(on)和Qg。同时,详细介绍了栅极驱动设计、PCB布局布线要点及调试安全步骤,旨在帮助开发者构建稳定高效的BLDC驱动系统。

weixin_29194817的博客 589

yocto系列讲解[实战篇]54 - 添加Qt5和生成带Qt5的SDK

为了满足读者盆友想看yocto如何添加qt5的需求,今天就开始尝试添加meta-qt5

fulinux的博客 3871

java juc面试题一般问什么?juc面试分析

对程序员来说,最重要的一个门槛就是面试了,虽然常有面试造核弹,工作拧螺丝的说法,但该要的面试还是要的,这里整理了一些java中juc相关面试题,快来了解下吧。 一、juc具体是? Java 5.0 提供了java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常见的实用工具类,用于定义类似于编程的自定义子系统,包括线程池、异步IO和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文的Collection实现等。 二、什么是CAS? CAS(Compare-A

javase1085的博客 2962

人工蜂群优化算法(ABC)详解:原理、实现与应用

人工蜂群算法(Artificial Bee Colony, ABC)是Karaboga于2005年提出的一种群体智能优化算法,其灵感来源于蜜蜂群体在自然界中高效的采蜜行为。该算法通过模拟雇佣蜂、观察蜂和侦察蜂三种角色的分工协作来寻找最优解。在ABC算法中,每个潜在的解决方案被视为一个“食物源”,其质量由适应度函数评估。雇佣蜂负责在已知优质食物源附近进行局部开采;观察蜂根据雇佣蜂分享的信息,以一定概率选择并跟进优质食物源;侦察蜂则随机探索新区域,防止种群陷入局部最优。

qq_42212808的博客 393

Java面试题(JUC)

JUC java.util.concurrent的缩写 线程的6种状态 创建,可运行,阻塞,等待,计时等待,终结 Lambda表达式 java1.8之后允许接口中有部分方法的实现,需要用default关键字描述方法 @FunctionalInterface 注解表示函数式接口(仅有一个抽象方法) 常见异常总结 ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常 StackOverflowError: 多线程案例

ShyTan的博客 1206

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【Java并发】JUC 常见基础面试题

文章目录JUC 面试题谈谈 volatile可见性验证为什么不保证原子性如何解决原子性JMM(Java 内存模型)可见性原子性有序性DCL (Double Check Lock)版本单例模式CAS(CompareAndSwap)Unsafe 类举例CAS 的缺点ABA问题集合类不安全问题公平锁和非公平锁可重入锁(递归锁)LockSupport原理使用及对比参考资料 JUC 面试题 谈谈 volatile volatile 是由 Java 虚拟机提供的一种轻量级的同步机制。 保证可见性 不保证原子性 禁止重

好好学习! 2427

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Java并发编程面试题53道-JUC

这个包(java.util.concurrent)极大地增强了Java在并发编程领域的支持,提供了一系列高级抽象如线程池(ThreadPoolExecutor)、并发集合(ConcurrentHashMap、CopyOnWriteArrayList等)、同步器(Semaphore、CountDownLatch、CyclicBarrier、ReentrantLock等)以及其他并发工具和框架。JUC让Java程序员可以更专注于业务逻辑的并发实现,而不用过多关注底层并发控制机制的实现细节。

nuist_NJUPT的博客 1950

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

java juc包面试题_【Java面试经验】JUC部分(对应周阳老师的面试教程)

写在最前:此篇文章对应周阳老师的大厂面试教程,几乎包含了大厂所有JUC部分的面试题,虽然自己无缘大厂,但是这些知识也让我在面试中得到了不错的评价。MarkDown文档下载地址:JUC面试个人博客地址:http://www.zwmuzhi.cn/结合另一篇文章食用更为妥当【Java面试经验】JAVA基础部分(从入门到放弃)JUCjava.util.concurrent的缩写线程的6种状态创建,可运行...

weixin_39948277的博客 457

JUC java并发面试题

1、synchronized的底层原理 java虚拟机里面的同步是基于进入和退出monitor对象实现的,无论是显式同步(同步代码块)还是隐式同步都是如此,同步方法并不是由monitorenter和monitorexit指令来实现同步的,而是由方法调用指令读取运行时常量池中的表结构的ACC_SYNCHRONIZED标志来隐式实现的; 同步代码块:monitorenter插入到同步代码块开始位置,monitorexit指令插入到同步代码块结束的位置,任何对象都有一个monitor与之关联,当且一个monito

老头的博客 4369

Java面试题汇总-集合篇+JUC篇

Hash 碰撞是指不同的数据通过哈希算法计算后得到相同的哈希值,映射到了哈希表的同一个位置,发生”碰撞”解决方案: 1.拉链法(链地址法): 将哈希表的每一个槽的位置变成一个链表,将哈希值相同的键存储到同一个链表中2.开放寻址法: 如果出现碰撞,就寻找哈希表下一个可用的位置. 3.再哈希法(双重哈希): 在出现碰撞时,使用第二个哈希函数计算新的索引位置, 减少碰撞的概率当一个共享资源被多个线程访问时进行保护的行为叫线程同步,让线程有序访问,目的是为了避免出现数据不一致等问题。延伸→如何保证线程同步?

csdn3043663729的博客 3311

java juc包面试题_Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue

Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue本文聊一下 JUC 下的 LinkedBlockingQueue 行列,先说说 LinkedBlockingQueue 行列的特色,然后再从源码的视点聊一聊 LinkedBlockingQueue 的首要完成~LinkedBlockingQueue 有以下特色:LinkedBlockingQueue 是堵塞行列,底层...

weixin_42443797的博客 262

java面试题JUC篇

java面试juc相关内容

qq_44658022的博客 839

Java多线程(JUC)-高频面试题

正是基于这样的困扰与思考,我决定亲自上阵,撰写一份与众不同的面试指南。这份指南不仅仅是对现有资源的简单汇总,更重要的是,它融入了我的个人理解和解读。我力求`回归技术书籍本身`,以一种层层递进的方式剖析复杂的技术概念,让那些看似枯燥乏味的知识点变得生动起来,并在我的脑海中构建起一套完整的知识体系。我希望通过这种方式,不仅能帮助自己在未来的技术面试中更加从容不迫,也能为同行们提供一份有价值的参考资料,使大家都能在这个过程中有所收获。

玦尘的博客 1910

java juc包面试题_面试系列 JUC并发包

CountDownLatch并行串行化,在多个线程运行中设置串行点。(程序员干活,最后由领导汇报)CountDownLatch(5) //初始化计数值,必须大于等于0latch.countDown() //计数值自减,如果当前值为0无操作 计数器需要在各个线程共享latch.await() //只要计数值不等于0就会blocked住当前线程latch.await(time.TimeUnit.MI...

weixin_42396918的博客 797

java面试题-JUC锁

JUC锁相关,Lock,AQS,ReentrantLock,ReentrantReadWriteLock等相关面试题

qq_33129875的博客 653

java面试题-JUC基础类介绍

JUC 相关类介绍

qq_33129875的博客 646

Java并发指南18:JUC常见面试题及答案

什么是 CAS 吗? CAS(Compare And Swap)指比较并交换。CAS算法CAS(V, E, N)包含 3 个参数,V 表示要更新的变量,E 表示预期的值,N 表示新值。在且仅在 V 值等于 E值时,才会将 V 值设为 N,如果 V 值和 E 值不同,则说明已经有其他线程做了更新,当前线程什么都不做。最后,CAS 返回当前 V 的真实值。Concurrent包下所有类底层都是依靠CAS操作来实现,而sun.misc.Unsafe为我们提供了一系列的CAS操作。 CAS 有什么缺点? AB

Java技术江湖 2479
上一篇: Java面试题(JUC&JVM)
下一篇: java面试题之JVM基础
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从现在开始壹并超

你的鼓励,我们就是hxd

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值