多个线程多个锁

 
/**
 * 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,
 * 那么这个类(对象或方法)就是线程安全的。
 *
 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”
 *
 * 当多个线程访问myThread的run方法时,以排队的方式进行处理
 * (这里排队是按照cpu分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法
 * 里的代码,首先是尝试获得锁,如果拿到锁,执行synchronized代码体内容;拿不到锁,这个
 * 线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。
 * (也就是会有锁竞争的问题)。
 */
public class MyThread extends Thread{

    private int count = 5;

    /**synchronized**/
    public void run(){
        count--;
        System.out.println(this.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }

}
执行结果:(多次执行发现结果不一样)
t3 count = 3
t1 count = 1
t4 count = 2
t2 count = 3
t5 count = 0
 
public class MyThread extends Thread{

    private int count = 5;

    /**synchronized**/
    public synchronized void run(){
        count--;
        System.out.println(this.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //排队是按照cpu分配的先后顺序而定的
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }

    /**
     * 执行结果:可能线程名不是按顺序的但是值是有序的
     * t2 count = 4
     * t1 count = 3
     * t3 count = 2
     * t4 count = 1
     * t5 count = 0
     */
}
 
 
/**
 * 多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁。
 * 分别获得锁之后,执行synchronized方法体的内容。
 *
 * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
 * 所以示例代码中那个线程先执行synchronized关键字的方法,那个线程就持有
 * 该方法所属对象的锁(lock),两个对象,线程获得就是两个不同的锁,他们互不影响。
 *
 * 有一种情况则是相同的锁,即在静态方法上加synchronized关键字,表示锁定.class类,
 * 类一级别的锁(独占.class类)
 */
public class MultiThread {

    /** static **/
    private int num = 0;
    /** static **/
    public synchronized void printNum(String tag){
        try{
            if(tag.equals("a")){
                num = 100;
                System.out.println("tag a , set num over");
                Thread.sleep(1000);
            }else{
                num = 200;
                System.out.println("tag b , set num over");
            }
            System.out.println("tag " + tag + ", num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        final MultiThread m1 = new MultiThread();
        final MultiThread m2 = new MultiThread();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                m1.printNum("a");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                m2.printNum("b");
            }
        });
        t1.start();
        t2.start();
    }

}

执行结果:

tag a , set num overtag b , set num overtag b, num = 200tag a, num = 100(等待一秒后输出)

public class MultiThread {

    /** static **/
    private static int num = 0;
    /** static **/
    public static synchronized void printNum(String tag){
        try{
            if(tag.equals("a")){
                num = 100;
                System.out.println("tag a , set num over");
                Thread.sleep(1000);
            }else{
                num = 200;
                System.out.println("tag b , set num over");
            }
            System.out.println("tag " + tag + ", num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        final MultiThread m1 = new MultiThread();
        final MultiThread m2 = new MultiThread();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                m1.printNum("a");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                m2.printNum("b");
            }
        });
        t1.start();
        t2.start();
    }
}
执行结果:

tag a , set num over
tag a, num = 100 (等待一秒后执行)
tag b , set num over
tag b, num = 200

public class MyObject {

    public synchronized void method1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**synchronized**/
    public synchronized void method2(){
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args){
        final MyObject mo = new MyObject();

        /**
         * t1线程先持有object对象的lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法
         * t1线程先持有object对象的lock锁,t2线程如果在这个时候调用对象中的同步方法则需等待,也就是同步
         */
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                mo.method1();
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                mo.method2();
            }
        },"t2");

        t1.start();
        t2.start();

    }

    /**
     * 执行结果:
     * t1
     * t2 (稍等4秒钟执行)
     */
}

public class MyObject {

    public synchronized void method1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**synchronized**/
    public void method2(){
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args){
        final MyObject mo = new MyObject();

        /**
         * t1线程先持有object对象的lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法
         * t1线程先持有object对象的lock锁,t2线程如果在这个时候调用对象中的同步方法则需等待,也就是同步
         */
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                mo.method1();
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                mo.method2();
            }
        },"t2");

        t1.start();
        t2.start();

    }

    /**
     * 执行结果:
     * t1
     * t2 
     * 执行完毕程序还在执行4秒后关闭
     */
}
/**
 * 在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为setValue/getValue方法
 * 同时加锁synchronized同步关键字,保证业务的原子性,不然会出现业务错误
 * (也从侧面保证业务的一致性)
 */
public class DirtyRead {

    private String username = "xiwang";
    private String password = "123";

    public synchronized void setValue(String username,String password){
        this.username = username;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.password = password;
        System.out.println("setValue最终结果:username = " + username + " , password = " + password);
    }

    public void getValue(){
        System.out.println("getValue最终结果:username = " + this.username + " , password = "  + this.password);
    }

    public static void main(String[] args) throws InterruptedException {
        final DirtyRead dr = new DirtyRead();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                dr.setValue("zhangsan","456");
            }
        });
        t1.start();
        Thread.sleep(1000);
        dr.getValue();
    }

    /**
     * 执行结果
     * getValue最终结果:username = zhangsan , password = 123
     * setValue最终结果:username = zhangsan , password = 456
     */

}
 

public class SyncDubbo1 {

    public synchronized void method1(){
        System.out.println("method1..");
        method2();
    }

    public synchronized  void method2(){
        System.out.println("method2..");
        method3();
    }

    public synchronized  void method3(){
        System.out.println("method3..");
    }

    public static void main(String[] args) {
        final SyncDubbo1 sd = new SyncDubbo1();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                sd.method1();
            }
        });
        t1.start();
    }
}
/**
 * 关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了
 * 一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。
 */
public class SynDubbo2 {

    static class Main{
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    static class Sub extends Main{
        public synchronized void operationSub(){
            try{
                while(i > 0){
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);
                    this.operationSup();
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                Sub sub = new Sub();
                sub.operationSub();
            }
        });
        t1.start();
    }

    /**
     * 执行结果:
     * Sub print i = 9
     * Main print i = 8
     * Sub print i = 7
     * Main print i = 6
     * Sub print i = 5
     * Main print i = 4
     * Sub print i = 3
     * Main print i = 2
     * Sub print i = 1
     * Main print i = 0
     */
}
 
/**
 * volatile概念:volatile关键字的主要作用是使变量在多个线程间可见。
 *
 * 在java中,每一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量值的
 * 拷贝。当线程执行时,他在自己的工作内存区中操作这些变量。为了存取一个共享的变量,一个
 * 线程通常先获取锁定并去消除他的内存工作区,把这些共享变量从所有线程的共享内存区中正确的
 * 装入到他自己所在的工作内存中,当线程解锁时保证该工作内存区中变量的值写回到共享内存中。
 *
 * 一个线程可以执行的操作有使用(use),赋值(assign),装载(load),存储(store),
 * 锁定(lock),解锁(unlock);
 *
 * 而主内存可以执行的操作有读(read),写(write),锁定(lock),解锁(unlock),每个操
 * 作都是原子的。
 *
 * volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去
 * 读取,从而实现了多个线程间的变量可见,也就是满足线程安全的可见性。
 */
public class RunThread extends Thread {

    private boolean isRunning = true;

    public void setRunning(boolean running) {
        this.isRunning = running;
    }

    public void run(){
        System.out.println("进入run方法...");
        while(isRunning == true){
            boolean a = isRunning;
        }
        System.out.println("线程终止");
    }

    public static void main(String[] args) throws InterruptedException {
        RunThread rt = new RunThread();
        rt.start();
        Thread.sleep(3000);
        rt.setRunning(false);
        System.out.println("isRunning的值已经被设置成了false");
        Thread.sleep(1000);
        System.out.println(rt.isRunning);
    }

    /**
     * 执行结果:
     * 进入run方法...
     * isRunning的值已经被设置成了false
     * false
     * 
     * 注意:程序一直在运行说明while一直正确
     */
}
 
public class RunThread extends Thread {

    private volatile boolean isRunning = true;

    public void setRunning(boolean running) {
        this.isRunning = running;
    }

    public void run(){
        System.out.println("进入run方法...");
        while(isRunning == true){
            boolean a = isRunning;
        }
        System.out.println("线程终止");
    }

    public static void main(String[] args) throws InterruptedException {
        RunThread rt = new RunThread();
        rt.start();
        Thread.sleep(3000);
        rt.setRunning(false);
        System.out.println("isRunning的值已经被设置成了false");
        Thread.sleep(1000);
        System.out.println(rt.isRunning);
    }

    /**
     * 执行结果:
     * 进入run方法...
     * isRunning的值已经被设置成了false
     * 线程终止
     * false
     */
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * volatile关键字只具有可见性,没有原子性。要实现原子性建议使用atomic类的系列对象,
 * 支持原子性操作(注意atomic类只保证本身方法原子性,并不保证多次操作的原子性)
 */
public class AtomicUse {


    private static AtomicInteger count = new AtomicInteger(0);

    public int multiAdd(){
        try{
            Thread.sleep(100);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        count.addAndGet(1);
        count.addAndGet(2);
        count.addAndGet(3);
        count.addAndGet(4);  // +10
        return count.get();
    }

    public static void main(String[] args){
        final AtomicUse su = new AtomicUse();

        List<Thread> ts = new ArrayList<Thread>();
        for (int i = 0 ; i < 100 ; i++ ){
            ts.add(new Thread(new Runnable() {
                public void run() {
                    System.out.println(su.multiAdd());
                }
            }));
        }

        for (Thread t : ts){
            t.start();
        }
    }
    
    /**
     * 10
     * 20
     * 30
     * 40
     * 50
     * 60
     * 70
     * 80
     * 90
     * 100
     * 110
     * 120
     * 130
     * 140
     * 150
     * 160
     * 170
     * 180
     * 190
     * 200
     * 210
     * 220
     * 230
     * 240
     * 250
     * 260
     * 270
     * 280
     * 290
     * 300
     * 310
     * 320
     * 330
     * 340
     * 350
     * 360
     * 370
     * 380
     * 390
     * 400
     * 410
     * 420
     * 430
     * 1000
     * 440
     * 990
     * 980
     * 970
     * 960
     * 950
     * 940
     * 930
     * 920
     * 910
     * 900
     * 890
     * 880
     * 870
     * 860
     * 850
     * 840
     * 830
     * 820
     * 810
     * 800
     * 790
     * 780
     * 770
     * 760
     * 750
     * 740
     * 730
     * 723
     * 710
     * 700
     * 690
     * 680
     * 670
     * 660
     * 650
     * 640
     * 631            //说明值不能保证原子性
     * 620
     * 610
     * 600
     * 590
     * 580
     * 570
     * 560
     * 550
     * 540
     * 530
     * 520
     * 510
     * 500
     * 490
     * 480
     * 470
     * 463
     * 450
     * 只能保证自身是原子性的,但不能保证整个方法是原子性的
     */

}
 
public class AtomicUse {


    private static AtomicInteger count = new AtomicInteger(0);

    public synchronized int multiAdd(){
        try{
            Thread.sleep(100);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        count.addAndGet(1);
        count.addAndGet(2);
        count.addAndGet(3);
        count.addAndGet(4);  // +10
        return count.get();
    }

    public static void main(String[] args){
        final AtomicUse su = new AtomicUse();

        List<Thread> ts = new ArrayList<Thread>();
        for (int i = 0 ; i < 100 ; i++ ){
            ts.add(new Thread(new Runnable() {
                public void run() {
                    System.out.println(su.multiAdd());
                }
            }));
        }

        for (Thread t : ts){
            t.start();
        }
    }

    /**
     * 10
     * 20
     * 30
     * 40
     * 50
     * 60
     * 70
     * 80
     * 90
     * 100
     * 110
     * 120
     * 130
     * 140
     * 150
     * 160
     * 170
     * 180
     * 190
     * 200
     * 210
     * 220
     * 230
     * 240
     * 250
     * 260
     * 270
     * 280
     * 290
     * 300
     * 310
     * 320
     * 330
     * 340
     * 350
     * 360
     * 370
     * 380
     * 390
     * 400
     * 410
     * 420
     * 430
     * 440
     * 450
     * 460
     * 470
     * 480
     * 490
     * 500
     * 510
     * 520
     * 530
     * 540
     * 550
     * 560
     * 570
     * 580
     * 590
     * 600
     * 610
     * 620
     * 630
     * 640
     * 650
     * 660
     * 670
     * 680
     * 690
     * 700
     * 710
     * 720
     * 730
     * 740
     * 750
     * 760
     * 770
     * 780
     * 790
     * 800
     * 810
     * 820
     * 830
     * 840
     * 850
     * 860
     * 870
     * 880
     * 890
     * 900
     * 910
     * 920
     * 930
     * 940
     * 950
     * 960
     * 970
     * 980
     * 990
     * 1000
     * synchronized 保证方法是正确的
     */

}
import java.util.ArrayList;
import java.util.List;

/**
 * 线程通信概念:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理
 * 就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信
 * 指挥,系统间的交互性会更强大,在提高cpu利用率的同时还会使开发人员对线程
 * 任务在处理的过程中进行有效的把控与监督。
 *
 *
 * 使用wait/notify方法实现线程间的通信。(注意这两个方法都是object的类的方法,
 * 换句话说java为所有的对象都提供了这两种方法)
 * 1.wait和notify必须配合synchronized关键字使用
 * 2.wait方法释放锁,notify方法不释放锁。
 */
public class ListAdd1 {

    private volatile static List list = new ArrayList();

    public void add(){
        list.add("qdxxwlc");
    }
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        final ListAdd1 list1 = new ListAdd1();

        Thread t1 = new Thread(new Runnable(){
            public void run() {
                try{
                    for(int i = 0 ; i < 10 ; i++){
                        list1.add();
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素。。");
                        Thread.sleep(500);
                    }
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable(){
            public void run() {
                while(true){
                    if(list1.size() == 5){
                        System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止。。");
                        throw new RuntimeException();
                    }
                }
            }
        },"t2");

        t1.start();
        t2.start();
    }

    /**
     * 运行结果:
     *
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * Exception in thread "t2" java.lang.RuntimeException
     * 当前线程:t1添加了一个元素。。
     * 当前线程收到通知:t2 list size = 5 线程停止。。
     *     at cn.fenghuo.jiqing.util.connect.ListAdd1$2.run(ListAdd1.java:51)
     *     at java.lang.Thread.run(Thread.java:745)
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     *
     */

}
 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * wait notify 方法,wait释放锁,notify不释放锁
 */
public class ListAdd2 {

    private volatile static List list = new ArrayList();

    public void add(){
        list.add("qdxxwlc");
    }
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        final ListAdd2 list2 = new ListAdd2();

        //1 实例化出来一个lock
        // 当使用wait和notify的时候,一定要配合着synchronized关键字使用
        final Object lock = new Object();

//        final CountDownLatch countDownLatch = new CountDownLatch(1);

        Thread t1 = new Thread(new Runnable(){
            public void run() {
                try{
                    synchronized (lock){
                        for (int i = 0 ; i < 10 ; i++){
                            list2.add();
                            System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素。。");
                            Thread.sleep(500);
                            if(list2.size() == 5){
                                System.out.println("已经发出通知。。");
//                                countDownLatch.countDown();
                                lock.notify();//不释放锁
                            }
                        }
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable(){
            public void run() {
                synchronized (lock){
                    if(list2.size() != 5){
                        try{
                            System.out.println("t2进入。。。");
                            lock.wait();//释放锁
//                            countDownLatch.await();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止。。");
                    throw new RuntimeException();
                }
            }
        },"t2");

        t2.start();
        t1.start();
    }

    /**
     * 运行结果:
     *
     * t2进入。。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 已经发出通知。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t2收到通知线程停止。。
     * Exception in thread "t2" java.lang.RuntimeException
     *     at cn.fenghuo.jiqing.util.connect.ListAdd2$2.run(ListAdd2.java:64)
     *     at java.lang.Thread.run(Thread.java:745)
     *     
     *     不是很及时的通知。
     */


}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * wait notify 方法,wait释放锁,notify不释放锁
 */
public class ListAdd2 {

    private volatile static List list = new ArrayList();

    public void add(){
        list.add("qdxxwlc");
    }
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        final ListAdd2 list2 = new ListAdd2();

        //1 实例化出来一个lock
        // 当使用wait和notify的时候,一定要配合着synchronized关键字使用
//        final Object lock = new Object();

        final CountDownLatch countDownLatch = new CountDownLatch(1);

        Thread t1 = new Thread(new Runnable(){
            public void run() {
                try{
//                    synchronized (lock){
                        for (int i = 0 ; i < 10 ; i++){
                            list2.add();
                            System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素。。");
                            Thread.sleep(500);
                            if(list2.size() == 5){
                                System.out.println("已经发出通知。。");
                                countDownLatch.countDown();
//                                lock.notify();//不释放锁
                            }
                        }
//                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable(){
            public void run() {
//                synchronized (lock){
                    if(list2.size() != 5){
                        try{
                            System.out.println("t2进入。。。");
//                            lock.wait();//释放锁
                            countDownLatch.await();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止。。");
                    throw new RuntimeException();
//                }
            }
        },"t2");

        t2.start();
        t1.start();
    }

    /**
     * 运行结果:
     *
     * t2进入。。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 已经发出通知。。
     * Exception in thread "t2" java.lang.RuntimeException
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t2收到通知线程停止。。
     *     at cn.fenghuo.jiqing.util.connect.ListAdd2$2.run(ListAdd2.java:64)
     *     at java.lang.Thread.run(Thread.java:745)
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     * 当前线程:t1添加了一个元素。。
     *
     * 跟锁一点关系都没有
     */


}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值