您现在的位置是:首页 > 正文

高并发多线程学习(一)

2024-04-01 01:35:40阅读 3

一,什么是高性能?
高性能主要体现在2个方面
1,响应
2,吞吐
响应是指着请求方发出请求后得到服务方的返回所花费的时候,这段过程称为响应。如果响应时间短 就说明性能好的一方面。也是底延时的效果。
吞吐就是指在一定的时候内容所响应的次数。次数越高吞吐量就越高,反之越低。
从这两个方面来看提提升响应是相对比较困难的,所以很多人会从提升吞吐量来提升性能。
因此市面上也有很多的这方面的框架比如:集群 负载均衡 都是优化吞吐量的框架。
当然从响应方面优化的也有:
缓存,jvm优化 都是从响应方面来提升性能。
分表分库 既从响应优化也从吞吐优化。
二, 线程开发
1,多线程开发时不是线程越多越好,因为CPU 在切换线程的时候是需要花费时间的,
通常是要根据CPU 的核数来决定线程的个数,最好是通过压测的方式来决定。

2,多线程和同步锁
synchronized 是一把悲观锁,也是互斥锁。
也就是加了这把锁后就只能同时允许一个线程在里面运行。加锁后就会由原来的多线程并发转变成线程排序执行。
悲观锁:虽然不一定会有人跟我抢着运行同一个代码,但我还是不放心,锁上了再说,不管别人。

另外还有乐观锁,也叫自旋锁 CAS (Compare and set) 就是乐观锁的一种实现。
乐观锁其实就是一种概念:可以理解为非常乐观来看待数据或某个变量是不会冲突的,是不会出错的,所以就没有悲观锁,而允许多线程同时来操作,但是数据会有错误,就通过自旋方式来解决处理。 所以自旋锁就是乐观锁的具体实现。

什么是自旋锁呢?
首先,每个线程都会先获取当前的值,接着走一个原子的 CAS 操作。原子的意思就是这个 CAS 操作一定是自己完整执行完的,不会被别人打断。
然后 CAS 操作里,会比较一下,现在的值是不是刚才获取到的那个值。如果是,说明没人改过这个值,然后设置成累加 1 之后的一个值。
同理,如果有人在执行 CAS 的时候,发现之前获取的值跟当前的值不一样,会导致 CAS 失败。失败之后,进入一个无限循环,再次获取值,接着执行 CAS 操作。

3,乐观锁 和悲观锁的使用
什么时候用悲观锁什么时候用乐观锁呢?
一般情况能用悲观锁就用悲观锁。
虽然cas 好像性能要高一点,但这是不一定的。如果线程非常多而且每个线程执行的代码花费时间长就会导致其他大量的线程一直空旋,这样就浪费了资源也降低了性能。
只有在线程不是非常多,每个线程执行时间短的情况下使用乐观锁比较好。

4,jdk1.5 后 synchronized 已经升级了
升级过程包括了
1,偏向锁
很多时候执行一段代码同一时间都是一个线程,根本不需要锁的竞争,在这种情况完全可以不需要开销锁竞争带来的消耗,可以避开竞争的过程。为了提升这种情况的性能就使用了偏向锁的概念。
当一个线程执行时就会在锁对象的头部添加一个标签也就是自己的线程ID,当这个线程再次出现的时候就会比对线程id ,如果相同就直接使用这把锁。如果线程二来了就需要比对一下对象的head的线程Id 是否相同,当然是不同的,就会找到线程1的栈贞信息判断是否存活,如果没有存活就会该对象为无锁状态,线程二就设为偏向锁。
如果这个时候线程1还活着在运行,就意味着有2个线程了就需要升级,此时就会暂停线程1升级为轻量级锁。或者线程1虽然活着但已经释放锁了那么线程二就变为偏向锁。
2,轻量级锁
当有少量的线程同时并发的时候,并且持锁的时间也不长的时候。因为阻塞线程需要CPU从用户态转到内核态,开销也比较大,就转为cas 锁。等待线程就自旋状态。
3,重量级锁
当线程自旋过多或线程数过大时就会升级为重量极锁,因为自旋会让cpu 消耗更大。
这个时候就会把没有锁的线程都阻塞。直到上一个线程运行完成释放锁。这样就会让响应时间变长。

5、线程的常用关键方法
1,volatile 有两个作用,1,线程可见性 2,禁止指令重排序
线程可见性就是多个线程在同时修改一个参数时,每个线程都能获得修改后的值。
指令重排序,会导致运行的结果异常。比如 DCL 单例。在非常高的并发时。可能就会获取到半初始化的对象。导致获取的数值错误。

**
 * DCL 单例
 */
public class Single {

    //volatile  的作用:保证线程的可见性 ;禁止cpu 指令重排序
    private static volatile Single single;
    public static Single getInstance() {
        // 其他的代码业务......
        if (single == null) {
            //当对象为Null 时 多个线程会在这里产生锁的竞争,最终只有一个线程获得一个锁。
            synchronized (Single.class) {
                if (single == null) {
                    //如果对象没有volatile cpu 在超高并发时可能会出现指令重排序
                    //就会导致半初始化的对象返回了出去。导致对象初始化数据错误。
                    single = new Single();
                }
            }
        }
        return single;
    }
}

2,synchronized 悲观锁 互斥锁
synchronized 包含的代码越少越好。性能由多线程变成了单线程。
3,cas 乐观锁 问题
会出现ABA问题,当一个线程自旋的时候,首先会获取一个原始数值,自旋回来后就会拿现在的值和之前的值对比,如果一样说明没人动过,就累计。不同就继续自旋。在这个过程中
就算获取的值和之前的一样有可能A 已经被其他线程修改后又变回来了,看上去一样。其实已经经历了一些故事了。要区别是否被改变就需要添加一个版本号,每改一次就添加一个版本。根据版本就直到是不是开始的你。

AtomicLong cas 乐观锁 比synchronized 的性能要高
LongAdder 分段锁 非常高的并发性能最高

   private static AtomicInteger atopicInteger=new AtomicInteger(0);
    public   void  m(){
        for(int i=0 ;i<10000;i++){
            atopicInteger.incrementAndGet();
        }
    }
    public static void main(String[] args) {
        AtopicInteger lambdaThread002=new AtopicInteger();
       List<Thread> threadList=new ArrayList<>();
        for (int i=0;i<10;i++){
            threadList.add(new Thread(lambdaThread002::m,"thread=="+i));
        }
        threadList.forEach((thread)->thread.start());
        threadList.forEach((o)->{
            try {
                o.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        System.out.println("count:"+atopicInteger.toString());
    }

4,synchronized 和 ReentrantLock
synchronized 是可以调用synchronized 的方法,锁是可以重入的。
是自动解锁
ReentrantLock 可以替代synchronized 需要手动解锁 在 finally里面
tryLock 尝试获取锁
ReentrantLock(true) 是公平锁。表示在缓存排队,有人就先上,没人就我上。
如果为false 就我直接上,跟你竞争抢。

CountDownLatch 门闩
当门闩的数值为0的时候才会执行


public class CountDownLatch {
    private static int count;
    public static  void countLatch() {
        Thread[] threads = new Thread[1000];
        java.util.concurrent.CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(threads.length);

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                   synchronized (CountDownLatch.class){
                        count++;
                   }

                    countDownLatch.countDown();
                }
            });
        }
        for (Thread thread : threads) thread.start();
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(count);
    }

    public static void main(String[] args) {
        CountDownLatch.countLatch();

    }
}



public class CountDownLatch001 {
    volatile List list = Collections.synchronizedList(new ArrayList<>());

    public void add(Object a) {
        list.add(a);
    }

    public int size() {
        return list.size();
    }
     //门闩 当countDown() 为0的时候就会往下执行
    static CountDownLatch t1countDownLatch = new CountDownLatch(1);
    static CountDownLatch t2countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) {
        CountDownLatch001 c = new CountDownLatch001();
        new Thread(() -> {
            System.out.println("t1启动");
            for (int i = 0; i < 10; i++) {
                System.out.println("t1add  " + i);
                c.add(new Object());
                if (c.size() == 5) {
                    try {
                        t2countDownLatch.countDown();
                        t1countDownLatch.await();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(() -> {
            System.out.println("T2 begin");
            while (true) {
                if(c.size()!=5){
                    try {
                        t2countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("T2 OVER");
                t1countDownLatch.countDown();
                break;

            }
        }).start();






    }
}

CyclicBarrier 当线程达到预定的数量后,统一执行一次

public class CyclicBarrier {
    static int bauch = 0;

    public static void main(String[] args) {
        java.util.concurrent.CyclicBarrier cyclicBarrier = new java.util.concurrent.CyclicBarrier(100, () -> {
            bauch++;
            System.out.println("第一批次执行:" + bauch);
        });

        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                try {
                    System.out.println("处理我的业务。。。");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }
}


ReentrantLock   

public class GongpingLock {
  static Lock lock=new ReentrantLock(true);//true 为公平锁
    private  static int count=0;
    public  static void   demo01(){
        for (int i=0;i<10000;i++){
            try {
                lock.lock();
                count++;
                System.out.println(Thread.currentThread().getName()+"  "+count);
            }catch (Exception e){

            }finally {
                lock.unlock();
            }

        }
    }
    public static void main(String[] args) {

        new Thread(GongpingLock::demo01).start();
        new Thread(GongpingLock::demo01).start();
        new Thread(GongpingLock::demo01).start();

    }
}

LockSupport  阻塞和唤醒工具
public class LockSupportTest {
    public static void main(String[] args) {
      Thread t=  new Thread(()->{
            for(int i=0;i<10;i++){
                System.out.println("sss=== "+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(i==5){
                    LockSupport.park();
                }
            }
        });
      t.start();

        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         LockSupport.unpark(t);
    }

}

/**
 * try lock
 */
public class TryLock {
    static Lock lock = new ReentrantLock();
    public static void demo01() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                Thread.sleep(1000);
                System.out.println(i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public static void demo02() {
        boolean flag=false;

        try {
            flag=lock.tryLock(5, TimeUnit.SECONDS);
            if(flag){
                System.out.println("我拿到锁了");
            }else {
                System.out.println("我没拿到锁,我走了");
            }
            System.out.println(flag);
                Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(TryLock::demo02).start();
     new Thread(TryLock::demo01).start();


    }
}

AtomicLong


public class LongAtopic {
    static volatile Long count = 0l;// sys 加锁
    static AtomicLong countAtomicLong = new AtomicLong(0L);//cas 乐观锁

    static LongAdder  longAdder=new LongAdder();//采用了分段锁,高并发时性能最快

    /**
     *
     */
    public static synchronized void countM() {
        for (int i = 0; i < 1; i++) {
                count++;
        }
    }
    public  static void countAtomic() {
        for (int i = 0; i < 10000; i++) {
            countAtomicLong.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long startAtomicLong=System.currentTimeMillis();
        Thread[] threads = new Thread[10000];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(LongAtopic::countAtomic);
        }
        for(Thread t:threads)t.start();
        for(Thread t:threads)t.join();
        long  costAtomicTime=System.currentTimeMillis()-startAtomicLong;
        System.out.println("countAtomicLong:" + countAtomicLong+" time:"+costAtomicTime);


        long startAddr=System.currentTimeMillis();
        Thread[] addrThreads=new Thread[10000];
        for(int k=0;k<addrThreads.length;k++){
            addrThreads[k]=new Thread(()->{
             for(int j=0;j<10000;j++){
                 longAdder.increment();
             }
            });
        }
        for(Thread t:addrThreads)t.start();
        for(Thread t:addrThreads)t.join();
       long  costTime=System.currentTimeMillis()-startAddr;
        System.out.println("LongAdder:" + longAdder+" time:"+costTime);


        long startSyn=System.currentTimeMillis();
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            threadList.add(new Thread(LongAtopic::countM));
        }
        threadList.forEach((thread) -> {
            thread.start();
        });
        threadList.forEach((thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        long  costSynTime=System.currentTimeMillis()-startSyn;
        System.out.println("Synchronized:" + count+" time:"+costSynTime);

    }


}

Phaser
当多个线程按阶段来执行就用phaser 。只有每个线程都走到了第一个阶段才会进入下一个阶段。

public class Phaser {
    static Random r=new Random();
    static  MarriagePhaser phaser=new MarriagePhaser();

    public static void main(String[] args) {
        phaser.bulkRegister(10);
        for(int i=0;i<8;i++){
            new Thread(new Person("person"+i)).start();
        }
        new Thread(new Person("新娘")).start();
        new Thread(new Person("新郎")).start();
    }


   static class Person implements Runnable{
        String name;
        public Person(String name){
            this.name=name;
        }

        public  void array(){
            try {
                Thread.sleep(1000);
                System.out.println("到达现场了"+name);
                phaser.arriveAndAwaitAdvance();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void eat(){
            try {
                Thread.sleep(1000);
                System.out.println("吃完了"+name);
                phaser.arriveAndAwaitAdvance();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void leave(){
            try {
                Thread.sleep(1000);
                System.out.println("离开了"+name);
                phaser.arriveAndAwaitAdvance();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void hug(){
            try {
                if("新郎".equals(name)||"新娘".equals(name)){
                    Thread.sleep(1000);
                    System.out.println("洞房了"+name);
                    phaser.arriveAndAwaitAdvance();
                }else {
                    phaser.arriveAndDeregister();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


        @Override
        public void run() {
            array();
            eat();
            leave();
            hug();
        }
    }
}



public class MarriagePhaser extends Phaser {

    @Override
    protected boolean onAdvance(int phase, int registeredParties) {
        switch (phase) {
            case 0:
                System.out.println("所有人都到齐了" + registeredParties);
                return false;
            case 1:
                System.out.println("所有人都吃完了" + registeredParties);
                return false;
            case 2:
                System.out.println("所有人都离开了" + registeredParties);
                return false;
            case 3:
                System.out.println("婚礼结束,夫妻同房!" + registeredParties);
                return true;
            default:
                return true;
        }
    }
}

ReentrantReadWriteLock
读写锁表示也有两个锁,一个是读写操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。

  • 在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在读取写锁后才能进行写入操作。
  • 即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。读写互斥, 读不能有写,写可以有读。
public class ReadWriteLockTest {


        private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();// 读写锁

    public static  void readLock(){
        try {
            lock.readLock().lock();
            Thread.sleep(1000);
            System.out.println("read");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }

    }


    public  static  void write(){
        try {
            lock.writeLock().lock();
            Thread.sleep(1000);
            readLock();
            System.out.println("write");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
    /* Runnable readB=()->readLock(lock);
        for(int i=0;i<10;i++){
            new Thread(readB).start();
        }
        Runnable writeB=()->write(lock);*/

   /*     for(int i=0;i<100;i++){
            new Thread(ReadWriteLockTest::readLock).start();
        }*/
        for(int i=0;i<10;i++){
            new Thread(ReadWriteLockTest::write).start();
        }
    }


}

Semaphore
限流

public class Semaphore {

    public static void main(String[] args) {

        java.util.concurrent.Semaphore semaphore=new java.util.concurrent.Semaphore(1);
        // true 为公平,说明多个线程是在队列里等待获取锁的
        java.util.concurrent.Semaphore semaphorgp=new java.util.concurrent.Semaphore(2,true);

        new Thread(()->{
            try {
                semaphore.acquire();
                Thread.sleep(2000);
                System.out.println("我来了one");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
            }
        }).start();


        new Thread(()->{
            try {
                semaphore.acquire();
                Thread.sleep(2000);
                System.out.println("我来了two");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
            }
        }).start();
    }
}


/**
 * 当一个线程运行到5后结束第二个线程,再结束第一个线程
 * Semaphore 的实现方式。 通过控制限流1个,启动2个线程后就会获取执行权限谁拿到谁执行
 * 思路: 只启动t1 线程 打印1到5 在启动t2线程 并join t2 执行完毕t2后, 在aequare 获取线程权限打印6-9
 */
public class SemaphoreTest001 {
    static Semaphore semaphore=new Semaphore(1);
    static  Thread t1;
    static  Thread t2;
    public static void main(String[] args) {
        t1= new Thread(()->{
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i=0;i<5;i++){
                System.out.println(i);

            }
            semaphore.release();
            try {
                t2.start();
                t2.join();
                semaphore.acquire();
                for(int i=5;i<10;i++){
                    System.out.println(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });


        t2= new Thread(()->{
            System.out.println("t2 OVER");
        });
        t1.start();

    }
}


/**
 * 当一个线程跑到第5秒的时候另外一个线程停止
 *
 */
public class VolatileTest001 {
    //添加了 volatile 线程之间就有可见性。就能拿到另外一个线程变化后的数据。但是如果没有sleep 1秒 仍然获取不到数值
    // 也就是必须要睡眠1秒后 另外一个线程才能拿到数值 这是一个奇怪的问题
   //volatile List list = new ArrayList();
   // 创建一个线程安全的集和
   volatile  List list= Collections.synchronizedList(new ArrayList<>());

    public void add(Object a) {
        list.add(a);
    }

    public int size() {
        return list.size();
    }

    public static void main(String[] args) {
        VolatileTest001 volatileTest001 = new VolatileTest001();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                volatileTest001.add(new Object());
                System.out.println("add" + i);
                try {
                   Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                if (volatileTest001.size() == 5) {
                    break;
                }
            }
            System.out.println("T2 OVER");

        }).start();

    }

}

package com.example.demo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.LockSupport;

/**
 *
 */
public class LockSupportTest001 {
    volatile List list = Collections.synchronizedList(new ArrayList<>());

    public void add(Object a) {
        list.add(a);
    }

    public int size() {
        return list.size();
    }
    static Thread thread1 = null;
    static Thread thread2 = null;
    public static void main(String[] args) {
        CountDownLatch001 c = new CountDownLatch001();

        thread2 = new Thread(() -> {
            System.out.println("T2 begin");
            while (true) {
                if (c.size() != 5) {
                    try {
                        LockSupport.park();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                LockSupport.unpark(thread1);
                System.out.println("T2 OVER");
                break;
            }
        });


       thread1= new Thread(() -> {
            System.out.println("t1启动");
            for (int i = 0; i < 10; i++) {
                System.out.println("t1add  " + i);
                c.add(new Object());
                if (c.size() == 5) {
                    try {
                        LockSupport.unpark(thread2);
                        LockSupport.park();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
       thread2.start();
       thread1.start();
    }



}

package com.example.demo;

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

public class CountDownLatch001 {
    volatile List list = Collections.synchronizedList(new ArrayList<>());

    public void add(Object a) {
        list.add(a);
    }

    public int size() {
        return list.size();
    }
     //门闩 当countDown() 为0的时候就会往下执行
    static CountDownLatch t1countDownLatch = new CountDownLatch(1);
    static CountDownLatch t2countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) {
        CountDownLatch001 c = new CountDownLatch001();
        new Thread(() -> {
            System.out.println("t1启动");
            for (int i = 0; i < 10; i++) {
                System.out.println("t1add  " + i);
                c.add(new Object());
                if (c.size() == 5) {
                    try {
                        t2countDownLatch.countDown();
                        t1countDownLatch.await();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(() -> {
            System.out.println("T2 begin");
            while (true) {
                if(c.size()!=5){
                    try {
                        t2countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("T2 OVER");
                t1countDownLatch.countDown();
                break;

            }
        }).start();






    }
}

网站文章

  • Maven学习总结(三)——使用Maven构建项目

    Maven学习总结(三)——使用Maven构建项目

    2019独角兽企业重金招聘Python工程师标准>>> ...

    2024-04-01 01:35:31
  • pycharm下载安装及创建工程

    pycharm下载安装及创建工程

    pycharm下载安装及创建工程1. 下载2. 创建工程PyCharm是一种Python IDE(Integrated Development Environment,集成开发环境),带有一整套可以帮...

    2024-04-01 01:35:08
  • IDEA spring boot 项目启动报异常:java.lang.ClassNotFoundException: javax.servlet.ServletContext 的解决办法! 热门推荐

    IDEA spring boot 项目启动报异常:java.lang.ClassNotFoundException: javax.servlet.ServletContext 的解决办法! 热门推荐

    修改【pom.xml 】文件中的配置

    2024-04-01 01:35:01
  • 2. 强化学习之——马尔科夫决策过程

    2. 强化学习之——马尔科夫决策过程

    目录 马尔科夫链 马尔科夫奖励过程 马尔科夫决策过程 马尔科夫决策过程中的策略估计 马尔科夫决策过程中的控制 策略迭代 值迭代 马尔科夫链 马尔科夫性:未来的转移跟过去是独立的,它只取决于现在 状态转...

    2024-04-01 01:34:52
  • 28. 找出字符串中第一个匹配项的下标

    28. 找出字符串中第一个匹配项的下标

    给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1。

    2024-04-01 01:34:28
  • (05)es索引创建

    (05)es索引创建

    非结构索引创建点击刷新出现如下:宽宽的框框代表分片,细细的框框代变备份点击信息--》索引信息结构索引创建1.点击复杂查询,输入json--点击易读--json检验--提交{"novel": {"properties": {"title": {"type": "text"}}}}e...

    2024-04-01 01:34:14
  • 已设置spring.main.allow-bean-definition-overriding=true依然报错问题

    可以另外再新建一个application.yml文件,将上面这个配置放在application.yml文件,然后Rebuild Project,启动项目,发现启动成功。查询了很多帖子,IDEA启动项目依然报错。可以试试这种方式解决。

    2024-04-01 01:34:07
  • 2 万字系统总结,带你实现 Linux 命令自由!

    2 万字系统总结,带你实现 Linux 命令自由!

    星标/置顶公众号????,硬核文章第一时间送达!前言Linux 的学习对于一个程序员的重要性是不言而喻的。前端开发相比后端开发,接触 Linux 机会相对较少,因此往往容易忽视它。但是学好它却...

    2024-04-01 01:33:42
  • 05-配置Swagger2生成API接口文档

    05-配置Swagger2生成API接口文档

    一、Swagger2介绍前后端分离开发模式中,api文档是最好的沟通方式。Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。及时性 (接口变更...

    2024-04-01 01:33:35
  • 关于vs code如何切换Python版本

    关于vs code如何切换Python版本

    大家在下载vs code后,配置Python环境时,一定会对切换电脑中的Python版本产生疑问,我以前也产生过,不过解决方法很简单。如下, 我这里是Python3.7.7版本,直接点击, 即可弹出选项选择。 ...

    2024-04-01 01:33:28