• JUC快速学习笔记


    JUC快速学习笔记

    狂神说JUC 个人学习笔记

    介绍

    JUC是指javaUtil包中的三个操作线程的包!

    并发操作

    不加锁

    • 方法
        //属性,方法
        private int number = 50;
        //买票的方式
        public void norSale() {
            if (number>0) {
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 测试
        @Test
        public void RunnableTest(){
            Ticket ticket = new Ticket();
            //Runnable接口为函数式接口
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.norSale();
                }
            },"a").start();
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.norSale();
                }
            },"b").start();
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.norSale();
                }
            },"c").start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 输出
    # 输出混乱
    b卖出了36票,剩余:35
    b卖出了34票,剩余:33
    b卖出了33票,剩余:32
    b卖出了32票,剩余:31
    b卖出了31票,剩余:30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ###加synchronized

    • 方法
        //买票的方式
        public synchronized void safeSale() {
            if (number>0) {
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 测试
        @Test
        public void RunnableTest(){
            Ticket ticket = new Ticket();
            //Runnable接口为函数式接口
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.safeSale();
                }
            },"a").start();
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.safeSale();
                }
            },"b").start();
            new Thread(()->{
                for (int i = 0; i < 40; i++) {
                    ticket.safeSale();
                }
            },"c").start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 输出
    # 输出有序
    a卖出了50票,剩余:49
    a卖出了49票,剩余:48
    a卖出了48票,剩余:47
    a卖出了47票,剩余:46
    a卖出了46票,剩余:45
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ###Lock锁(接口)

    默认非公平锁:十分不公平可以插队;
    公平锁:先来后到。

    • 上锁
        Lock lock = new ReentrantLock();
        // Lock买票
        public synchronized void safeLockSale() {
            lock.lock();//加锁
            try {
                //业务代码
                if (number>0) {
                    System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 测试和输出与加Synchronize基本一致

    • Sychronized和lock的区别

    1.Sychronized 内置的java关键字,Lock锁是一个java类

    2.Sychronized 无法判断获取锁的状态,Lock锁可以判断是否获取到了锁.

    3.Sychronized 会自动释放锁lock必须手动释放锁,如果不释放锁,死锁

    4.Sychronized 线程一(获得锁,阻塞),线程二(等待,傻傻的等),Lock锁就不一定会等待下去.

    5.Sychronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以中断锁,非公平(可以自己设置)

    6.Sychronized 适合锁少量的代码的同步问题,Lock适合锁大量的代码同步问题.

    线程通信,生产者消费者问题

    Sychronized版

    Sychronized版主要使用wait实现线程阻塞/等待,通过使用notifyAll来唤醒其他线程

    • 实体类
    @lombok.Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class Data {
        private int num = 0;
        public synchronized void increment() throws InterruptedException {
            while (num != 0){
                //等待
                this.wait();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+">="+num);
            //通知其他线程,我加一完毕了
            this.notifyAll();
        }
    
        public synchronized void decrement() throws InterruptedException {
            while (num == 0){
                this.wait();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+">="+num);
            //通知其他线程,我减一完毕
            this.notifyAll();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 测试
        @Test
        public void ProduceAndConsumerTest(){
            Data data = new Data();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    try {
                        data.increment();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    try {
                        data.increment();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    try {
                        data.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"B").start();
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    JUC版

    在juc版本中使用condition.await()进行等待,使用condition.signalAll()进行唤醒

    • 基本操作
    @lombok.Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class Data {
        private int num = 0;
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
    
        public void increment() throws InterruptedException {
            lock.lock();
            try{
                while (num != 0){
                    //等待
                    condition.await();
                }
                num++;
                System.out.println(Thread.currentThread().getName()+">="+num);
                //通知其他线程,我加一完毕了
                condition.signalAll();
            }finally {
                lock.unlock();
            }
    
        }
    
        public synchronized void decrement() throws InterruptedException {
            lock.lock();
            try{
                while (num == 0){
                    //等待
                    condition.await();
                }
                num--;
                System.out.println(Thread.currentThread().getName()+">="+num);
                //通知其他线程,我减一完毕了
                condition.signalAll();
            }finally {
                lock.unlock();
            }
        }
        
        
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • condition实现精准通知唤醒
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * A->B->C
     */
    public class C {
        public static void main(String[] args) {
            Data3 data3 = new Data3();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    data3.printA();
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    data3.printB();
                }
            },"B").start();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    data3.printC();
                }
            },"C").start();
        }
    }
    class Data3{
        private Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
        private int num =1; //1A,2B,3C
        public void printA() {
            lock.lock();
            try {
                //业务,判断,执行,通知
                while (num != 1){
                    condition1.await();
                }
                System.out.println("aaaaaaaaaaaa");
                //唤醒指定的人,B
                num =2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void printB() {
            lock.lock();
            try {
                while (num != 2){
                    condition2.await();
                }
                System.out.println("bbbbbbbbbbbb");
                //唤醒指定的人,B
                num =3;
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void printC() {
            lock.lock();
            try {
                while (num != 3){
                    condition3.await();
                }
                System.out.println("ccccccccccc");
                //唤醒指定的人,B
                num =1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    常用辅助类

    CountDownLatch

        public void CountDownLatchTest() throws Exception{
            //总数是6
            CountDownLatch countDownLatch = new CountDownLatch(6);
            for (int i = 0; i < 6; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"go out");
                    countDownLatch.countDown();
                },String.valueOf(i)).start();
            }
            //等待计数器归零才会向下执行
            countDownLatch.await();
            System.out.println("end");
            countDownLatch.countDown();//-1
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    CyclicBarrier

    和CountDownLatch相反

    • 代码
        @Test
        public void CyclicBarrierTest() throws Exception{
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
                System.out.println("finish");
            });
            for (int i = 0; i <= 7; i++) {
                final int temp = i;
                //lambda不能直接拿到for循环中的i
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"collect"+temp);
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 输出
    Thread-0collect0
    Thread-1collect1
    Thread-2collect2
    Thread-3collect3
    Thread-5collect5
    Thread-4collect4
    Thread-6collect6
    finish
    Thread-7collect7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Semaphore

    可以理解为CountDownLatch和CyclicBarrier的结合体,规定一个容量,允许增加或者减少

    • 代码
            Semaphore semaphore = new Semaphore(3);
            for (int i = 1; i <= 6; i++) {
                new Thread(()->{
                    //acquire()
                    try {
                        semaphore.acquire();//获得,如果满了,会等待被释放为止
                        System.out.println(Thread.currentThread().getName()+"抢到");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName()+"离开");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();//释放
                    }
                },String.valueOf(i)).start();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 输出
    1抢到
    4抢到
    3抢到
    1离开
    4离开
    3离开
    2抢到
    6抢到
    5抢到
    2离开
    5离开
    6离开
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ReadWriteLock

    读锁和写锁相互之间排斥:

    独占锁(写锁) 一次只能被一个线程占有

    共享锁(读锁) 可以同时被多个线程占有

    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    //ReadWriteLock
    public class ReadWriteLockDemo {
        //    自定义缓存
        public static void main(String[] args) {
            MyCatchLock myCatch = new MyCatchLock();
    //写入
            for (int i = 0; i <= 5; i++) {
                final int temp = i;
                new Thread(()->{
                    myCatch.put(temp+"",temp+"");
                },String.valueOf(i)).start();
            }
            //读取
            for (int i = 0; i <=5; i++) {
                final int temp = i;
                new Thread(()->{
                    myCatch.get(temp+"");
                },String.valueOf(i)).start();
            }
        }
    }
    class MyCatch {
        private volatile Map<String,Object> map = new HashMap<>(0);
        //存
        public void put(String key,Object value) {
            System.out.println(Thread.currentThread().getName()+"写入"+value);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"写入成功");
        }
        //取
        public void get(String key) {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取成功");
        }
    }
    //加锁的
    class MyCatchLock {
        private volatile Map<String,Object> map = new HashMap<>(0);
        //读写锁更加细粒度的控制
        private ReadWriteLock lock = new ReentrantReadWriteLock();
        private Lock lock1 = new ReentrantLock();
    
        //存,写的时候,只希望同时有一个线程写
        public void put(String key,Object value) {
            lock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName()+"写入"+value);
                map.put(key, value);
                System.out.println(Thread.currentThread().getName()+"写入成功");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.writeLock().unlock();
            }
        }
        //取,读,所有的人都可以读
        public void get(String key) {
            lock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName()+"读取"+key);
                Object o = map.get(key);
                System.out.println(Thread.currentThread().getName()+"读取成功");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.readLock().unlock();
            }
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 输出
    1写入1
    1写入成功
    0写入0
    0写入成功
    3写入3
    3写入成功
    4写入4
    4写入成功
    2写入2
    2写入成功
    5写入5
    5写入成功
    0读取0
    0读取成功
    5读取5
    2读取2
    2读取成功
    4读取4
    4读取成功
    1读取1
    1读取成功
    3读取3
    3读取成功
    5读取成功
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    BlockingQueue

    队列的基本操作

        @Test
        public void ArrayBlockingQueueTest () {
            //队列的大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(arrayBlockingQueue.add("a"));
            System.out.println(arrayBlockingQueue.add("b"));
            System.out.println(arrayBlockingQueue.add("c"));
            System.out.println(arrayBlockingQueue.element());//查看队首元素
            //ava.lang.IllegalStateException
    //        System.out.println(arrayBlockingQueue.add("d"));
    //        System.out.println("=============");
            //队列移除顺序
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            //java.util.NoSuchElementException
            System.out.println(arrayBlockingQueue.remove());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    阻塞等待

        @Test
        public void test3 () throws InterruptedException {
            //队列的大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            arrayBlockingQueue.put("a");
            arrayBlockingQueue.put("b");
            arrayBlockingQueue.put("c");
    //        arrayBlockingQueue.put("d");队列没有位置,一直阻塞
            System.out.println("=============");
            //队列移除顺序
            System.out.println(arrayBlockingQueue.take());
            System.out.println(arrayBlockingQueue.take());
            System.out.println(arrayBlockingQueue.take());
            System.out.println(arrayBlockingQueue.take());//没有这个元素,一直阻塞
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    超时等待

        @Test
        public  void test4 () throws InterruptedException {
            //队列的大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(arrayBlockingQueue.offer("a"));
            System.out.println(arrayBlockingQueue.offer("b"));
            System.out.println(arrayBlockingQueue.offer("c"));
            //等待超过两秒退出
            arrayBlockingQueue.offer("d", 2,TimeUnit.SECONDS);
            System.out.println(arrayBlockingQueue.poll());
            System.out.println(arrayBlockingQueue.poll());
            //等待超过两秒九退出
            System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    线程池

    • 线程池的好处

    1.降低资源的消耗

    2.提高响应速度

    3.方便管理

    线程可以复用,可以控制最大并发量,管理线程

    线程池:三大方法,7大参数,4种拒绝策略

    Executors创建线程池

        @Test
        public  void ExecutorsTest () throws InterruptedException {
                //Executors工具类,三大方法
                ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
                for (int i = 0; i < 10; i++) {
                    //使用了线程池之后,使用线程池来创建线程
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName()+"ok");
                    });
                }
                //线程池用完,程序结束,关闭线程池
                try {
                    threadPool.shutdown();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    手动创建线程池

        public static void main(String[] args) {
            //Executors工具类,三大方法
    //        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
    //        ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小得线程池
    //        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变
            //自定义线程池,工作
    
    
            //七个参数
    //    public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
    //        int maximumPoolSize,//最大核心线程大小
    //        long keepAliveTime,//超时了没人用就会释放
    //        TimeUnit unit,//超时单位
    //        BlockingQueue workQueue,//阻塞队列
    //        ThreadFactory threadFactory,//线程工厂,创建线程,一般不用动
    //        RejectedExecutionHandler handler) {//拒绝策略
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                    5,
                    3,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
    //                new ThreadPoolExecutor.AbortPolicy()//银行满了还有人进来,不处理这个人,抛出异常
    //                new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,丢掉任务
    //                new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里
                    new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早得竞争,也不会抛出异常
            );
            //最大承载:队列+max值
            for (int i = 0; i < 12; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
            //线程池用完,程序结束,关闭线程池
            try {
                threadPoolExecutor.shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    输出结果

    pool-1-thread-1ok
    pool-1-thread-4ok
    pool-1-thread-5ok
    pool-1-thread-3ok
    pool-1-thread-2ok
    pool-1-thread-5ok
    pool-1-thread-4ok
    pool-1-thread-1ok
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    TypeError: can only concatenate list (not “int“) to list
    基于php+thinkphp的网上书店购物商城系统
    【每日一题】1498. 满足条件的子序列数目
    Arcpy入门教程02:计算两景影像或DEM的差值并输出差值大于给定阈值的像素位置
    pytest结合Allure生成测试报告
    Spring Cloud灰度部署
    【软件测试】Requests库中处理cookie的几种方式
    vue 实现自定义主题切换+日夜切换
    C++二级指针的指向与解引用
    可靠的分布式KV存储产品-ETCD-初见
  • 原文地址:https://blog.csdn.net/qq_50665031/article/details/125891887