Juc01_多线程概述、四种实现方式、常用方法API、生命周期、买票案例、synchronized锁

目录

本章讲述内容:多线程概述、四种实现方式、常用方法API、生命周期、买票案例、synchronized锁

①. 多线程的概述  

②. 多线程的实现方式

①. 继承Thread

②. 实现Runnable接口

③. Callable接口(创建线程)

④. 线程池

③. 设置和获取线程名称

④. 线程优先级(setPriority)

⑤. 线程控制(sleep、join、setDeamon)

⑥. 线程的生命周期

⑦. 线程同步

①. 买票案例出现的两个问题

②. 同步代码块synchronized

③. 同步方法


本章讲述内容:多线程概述、四种实现方式、常用方法API、生命周期、买票案例、synchronized锁

①. 多线程的概述  


①. 为什么使用多线程及其重要


摩尔定律失效(硬件方面):(我们身为伟大的javer!必须了解背景hhh)
(1). 集成电路上可以容纳的晶体管数目在大约每经过18个月便会增加一倍,可是从2003年开始CPU主频已经不再翻倍,而是采用多核而不是更快的主频
(2).主频不再提高且核数不断增加的情况下,要想让程序更快就要用到并行或并发编程
高并发系统,异步+回调的生产需求(软件方面)


②. 进程、线程、管程(monitor 监视器)


线程就是程序执行的一条路径

一个进程中可以包含多条线程
多线程并发执行可以提高程序的效率,可以同时完成多项工作
管程:Monitor(监视器),也就是我们平时所说的锁
(1). Monitor其实是一种同步机制,它的义务是保证(在同一时间)只有一个线程可以访问被保护的数据和代码
(2). JVM中同步时基于进入和退出的监视器对象(Monitor,管程),每个对象实例都有一个Monitor对象。
(3). Monitor对象和JVM对象一起销毁,底层由C来实现

③. 多线程并行和并发的区别


并行:就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行(必须多核CPU)
并发:是指两个任务都请求运行,而处理器只能接收一个任务,就是把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行(12306抢票的案例)


④. wait | sleep的区别?功能都是当前线程暂停,有什么区别?

大粗话来说
wait:放开手去睡,放开手里的锁;wait是Object类中的方法
sleep:握紧手去睡,醒了手里还有锁;sleep是Thread中的方法


⑤. synchronized 和 lock的区别?

(1). 原始构成
 a. synchronized是关键字属于JVM层面
 monitor对象,每个java对象都自带了一个monitor,需要拿到monitor对象才能做事情
 monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖monitor对象,
 只能在同步块或方法中才能调用wait/notify等方法),进入
 monitorexit:退出
 b. lock是api层面的锁,主要使用ReentrantLock实现


(2). 使用方法
 a. synchronized不需要用户手动释放锁,当synchronized代码完成后系统会自动让线程释放
对锁的占用
 b. ReentrantLock则需要用户手动释放锁若没有主动释放锁,就有可能会导致死锁的现象


(3). 等待是否可中断?
 a. synchronized不可中断,除非抛出异常或者正常运行完成
 b. ReentrantLock可中断
 (设置超时时间tryLock(long timeout,TimeUnit unit),调用interrupt方法中断)


(4). 加锁是否公平
 a. synchronized非公平锁
 b. ReentrantLock两者都可以,默认是非公平锁,构造方法可以传入boolean值,true为公平锁,
 false为非公平锁


(5). 锁绑定多个Condition
 a.synchronized没有
 b.ReentrantLock用来实现分组唤醒需要唤醒线程们,可以精确唤醒,而不是像synchronized要么
 随机唤醒一个\要么多个

②. 多线程的实现方式

①. 继承Thread

	//注意:打印出来的结果会交替执行
	public class ThreadDemo{
	    public static void main(String[] args) {
	        //4.创建Thread类的子类对象
	        MyThread myThread=new MyThread();
	        //5.调用start()方法开启线程
	        //[ 会自动调用run方法这是JVM做的事情,源码看不到 ]
	        myThread.start();
	        for (int i = 0; i < 100; i++) {
	            System.out.println("我是主线程"+i);
	        }
	    }
	}
	class MyThread extends Thread{
	    //2.重写run方法
	    public void run(){
	        //3.将要执行的代码写在run方法中
	       for(int i=0;i<100;i++){
	           System.out.println("我是线程"+i);
	       }
	    }
	}

②. 实现Runnable接口

  • ①. 源码分析如下:
    public class RunnableDemo {
        public static void main(String[] args) {
            //4.创建Runnable的子类对象
            MyRunnale mr=new MyRunnale(); 
            //5.将子类对象当做参数传递给Thread的构造函数,并开启线程
            //MyRunnale taget=mr; 多态
            new Thread(mr).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("我是主线程"+i);
            }
        }
    }
    
    //1.定义一个类实现Runnable
    class MyRunnale implements Runnable{
        //2.重写run方法
        @Override
        public void run() {
            //3.将要执行的代码写在run方法中
            for (int i = 0; i < 1000; i++) {
                System.out.println("我是线程"+i);
            }
        }
    }
    

  • ②. 两种实现多线程方式的区别

    (1).查看源码
    a.继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()
    方法
    b.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用
 run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),
 运行时执行的是子类的run()方法
    (2).继承Thread
    a.好处是:可以直接使用Thread类中的方法,代码简单
    b.弊端是:如果已经有了父类,就不能用这种方法
    (3).实现Runnable接口
    a.好处是:即使自己定义的线程类有了父类也没有关系,因为有了父类可以实现接口,而且接口
可以多现实的
    b.弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,
代码复杂

③. Callable接口(创建线程)

①. Callable接口中的call方法和Runnable接口中的run方法的区别

1.是否有返回值:(Runnable接口没有返回值 Callable接口有返回值)
2.是否抛异常:(Runnable接口不会抛出异常 Callable接口会抛出异常)
落地方法不一样,一个是call() ,一个是run()

  • ②. Future接口概述
  1. FutureTask是Future接口的唯一的实现类
  2. FutureTask同时实现了Runnable、Future接口。它既可以作为Runnable被线程执行,又可以作为Futrue得到Callable的返回值
	/*
	创建线程的方式三: 实现callable接口 ---JDK 5.0 新增
	1.创建一个实现Callable接口的实现类
	2.实现call方法,将此线程需要执行的操作声明在call()中
	3.创建callable接口实现类的对象
	4.将此callable的对象作为参数传入到FutureTask构造器中,创建FutureTask的对象
	5.将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用star
	6.获取callable接口中call方法的返回值
	* */
	public class ThreadNew {
	    public static void main(String[] args) {
	        //3.创建callable接口实现类的对象
	        NumThead m=new NumThead();
	        //4.将此callable的对象作为参数传入到FutureTask构造器中,创建FutureTask的对象
	        
	        FutureTask futureTask = new FutureTask(m);
	        //5.将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
	        //FutureTask类继承了Runnable接口
	        //new Runnable = futrueTask;
	        new Thread(futureTask).start();
	
	        //6.获取callable接口中call方法的返回值
	        try {
	            //get()方法返回值即为FutureTask构造器参数callable实现类重写的call方法的返回值
	            Object sum = futureTask.get();
	            System.out.println("总和是:"+sum);
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
	
	}
	//1.创建一个实现Callable接口的实现类
	class  NumThead implements Callable{
	   // class  NumThead implements Callable<Integer>{
	    //2.实现call方法,将此线程需要执行的操作声明在call()中
	    @Override
	    public Object call() throws Exception {
	    //public Integer call() throws Exception {
	        int sum=0;
	        for(int i=1;i<=100;i++){
	            System.out.println(i);
	            sum+=i;
	        }
	        return sum;
	    }
	}
③.FutureTask原理解析

有了Runnable,为什么还要有Callable接口?

我们假设一共有四个程序需要执行,第三个程序时间很长 | Runnable接口会按照顺序去执行,会依次从上到下去执行,会等第三个程序执行完毕,才去执行第四个 | Callable接口会把时间长的第三个程序单独开启一个线程去执行,第1、2、4 线程执行不受影响
比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务。子线程就去做其他的事情,过一会儿才去获取子任务的执行结果

    例子:
    (1). 老师上着课,口渴了,去买水不合适,讲课线程继续,我可以单起个线程找班长帮忙
    买水,水买回来了放桌上,我需要的时候再去get。
    (2). 4个同学,A算1+20,B算21+30,C算31*到40,D算41+50,是不是C的计算量有点大啊,
    FutureTask单起个线程给C计算,我先汇总ABD,最后等C计算完了再汇总C,拿到最终结果
    (3). 高考:会做的先做,不会的放在后面做
 

  • ④. 注意事项
  1. get( )方法建议放在最后一行,防止线程阻塞(一旦调用了get( )方法,不管是否计算完成都会阻塞)
  2. 一个FutureTask,多个线程调用call( )方法只会调用一次
  3. 如果需要调用call方法多次,则需要多个FutureTask
    public class CallableDemo  {
        public static void main(String[] args) throws Exception{
            CallAble c=new CallAble();
            FutureTask<Integer> futureTask=new FutureTask<>(c);
    
            new Thread(futureTask,"线程A").start();
            new Thread(futureTask,"线程B").start();
            Integer integer = futureTask.get();
            System.out.println("integer = " + integer);
        }
    }
    class CallAble implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("欢迎你调用call方法");
            return 6;
        }
    }
    

 

  • ⑤. isDone()轮询(后面我们会用CompletableFuture来解决get( )阻塞的问题)
  1. 轮询的方式会消耗无畏的CPU资源,而且也不见得能及时地得到计算的结果
  2. 如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞
public class FutureTaskTest {
    public static void main(String[] args) throws Exception{
        FutureTask futureTask = new FutureTask(()->{
            try { TimeUnit.SECONDS.sleep(3);  } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t"+"coming......");
            return 1024;
        });
        new Thread(futureTask).start();
        //1.果futureTask.get()放到main线程前面,会导致main线程阻塞
        //Object o = futureTask.get();

        /*Object o = futureTask.get();//不见不散,只要出现了get()方法就会阻塞
        System.out.println("不见不散,只要出现了get()方法就会阻塞,获取到的值为:"+o);*/
        //2.过时不候
//        System.out.println(Thread.currentThread().getName()+"\t"+"线程来了.....");
//        Object o2 = futureTask.get(2L, TimeUnit.SECONDS);
        //3.使用轮询
        while(true){
            if(futureTask.isDone()){
                System.out.println("使用轮询来解决,值为:"+futureTask.get());
                break;
            }else{
                System.out.println("阻塞中**********");
            }
        }
    }
}

④. 线程池

Juc05_线程池概述、创建方式、七大参数、底层工作原理、拒绝策略

③. 设置和获取线程名称

  • ①. void setName(String name):将此线程的名称更改为等于参数 name
      //FileWriter
      MyThread my1 = new MyThread();

      //void setName(String name):将此线程的名称更改为等于参数 name
      my1.setName("高铁");

      my1.start();
  • ②. String getName( ):返回此线程的名称

注意:要是类没有继承Thread,不能直接使用getName( ) ;要是没有继承Thread,要通过Thread.currentThread得到当前线程,然后调用getName( )方法

  • ③. static Thread currentThread​( )返回对当前正在执行的线程对象的引用
  • .④ 通过构造函数设置线程名称
  • Thread(String name):通过带参构造进行赋值
    Thread(Runnable target , String name)

  • public class MyThread extends Thread {
        public MyThread() {}
    
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(getName()+":"+i);
            }
        }
    }
    //Thread(String name)
    MyThread my1 = new MyThread("高铁");
    MyThread my2 = new MyThread("飞机");
    my1.start();
    my2.start();
    

    ④. 线程优先级(setPriority)

  • ①. 线程有两种调度模型 [ 了解 ]

分时调度模式:所有线程轮流使用CPU的使用权,平均分配每个线程占有CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些 [ Java使用的是抢占式调度模型 ]


②. Thread类中设置和获取线程优先级的方法


public final void setPriority(int newPriority):更改此线程的优先级
public final int getPriority():返回此线程的优先级
a. 线程默认优先级是5;线程优先级范围是:1-10; b. 线程优先级高仅仅表示线程获取的CPU时间的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果

      ThreadPriority tp1 = new ThreadPriority();
      ThreadPriority tp2 = new ThreadPriority();
      ThreadPriority tp3 = new ThreadPriority();

      tp1.setName("高铁");
      tp2.setName("飞机");
      tp3.setName("汽车");
      //设置正确的优先级
      tp1.setPriority(5);
      tp2.setPriority(10);
      tp3.setPriority(1);

      tp1.start();
      tp2.start();
      tp3.start();

⑤. 线程控制(sleep、join、setDeamon)

  • ①. static void sleep(long millis):使当前正在执行的线程停留(暂停执行)指定的毫秒数 (休眠线程)
  • ②. void join():当前线程暂停,等待指定的线程执行结束后,当前线程再继续 (相当于插队加入)
    void join(int millis):可以等待指定的毫秒之后继续 (相当于插队,有固定的时间)
    ③. void yield():让出cpu的执行权(礼让线程)
    ④.void setDaemon​(boolean on):将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出(守护线程)(相当于象棋中的帅,要是帅没了,别的棋子都会没用了)

1.守护线程是区别于用户线程哈,用户线程即我们手动创建的线程,而守护线程是程序运行的时候在后台提供一种通用服务的线程。垃圾回收线程就是典型的守护线程


2.守护线程拥有自动结束自己生命周期的特性,非守护线程却没有。如果垃圾回收线程是非守护线程,当JVM 要退出时,由于垃圾回收线程还在运行着,导致程序无法退出,这就很尴尬。这就是为什么垃圾回收线程需要是守护线程
3.t1.setDaemon(true)一定要在start( )方法之前使用

    //守护线程和非守护线程的区别是
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()-> {
                while (true) {
                    try {
                        Thread.sleep(1000);
                        System.out.println("我是子线程(用户线程.I am running");
                    } catch (Exception e) {
                    }
                }
        });
        //标记为守护线程,setDaemon要在start()方法之前使用
        t1.setDaemon(true);
        //启动线程
        t1.start();

        Thread.sleep(3000);
        System.out.println("主线程执行完毕...");
    }

⑥. 线程的生命周期

①. 新建:就是刚使用new方法,new出来的线程

②. 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行

③. 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能

④. 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态
比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态

⑤. 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源

⑥. 完整的生命周期图如下:

⑦. 线程同步

①. 买票案例出现的两个问题

  • ①. 出现的问题:①. 相同票数出现多次;②.出现了负票

  • ②. 代码展示

    public class SellTicket implements Runnable {
        //定义一个成员变量表示有100张票
        private int tickets=100;
        public void run(){
         while (true){
             if(tickets>0){
                 try {
                     //通过sleep()方法来等待
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets--+"张票");
             }else{
                 //System.out.println("");
             }
         }
        }
    }
    @SuppressWarnings("all")
    public class SellTicketDemo {
        public static void main(String[] args) {
            SellTicket st = new SellTicket();
    
            Thread t1 = new Thread(st, "窗口1");
            Thread t2 = new Thread(st, "窗口2");
            Thread t3 = new Thread(st, "窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

  • ③. 原因分析:
  • 为什么会出现相同的票
  • 为什么会出现负票

 

②. 同步代码块synchronized

  • ①. 为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)
  1. 是否有多线程坏境
  2. 是否有共享数据
  3. 是否有多条语句操作共享数据
  • ②. 如何解决多线程安全问题
  1. 基本思想:让程序没有安全问题的坏境
  2. 把多条语句操作的共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
  • ③. 怎么锁起来呢?
    synchronized(任意对象):相当于给代码加锁了,任意对象就可以看成是一把锁

  • ④. 同步的好处和弊端

  1. 好处:解决了多线程的数据安全问题
  2. 弊端:当线程很多时,因为每个线程都会判断同步上的锁,这是很浪费资源的,无形中会降低程序的运行效率
    public class SellTicket implements Runnable {
        //定义一个成员变量表示有100张票
        private int tickets=100;
        
        private Object obj=new Object();
    
        public void run(){
         while (true){
           //这里放的锁要是同一把锁才可以
           synchronized(obj){
               if(tickets>0){
                   try {
                       //通过sleep()方法来等待
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets--+"张票");
               }else{
                   //System.out.println("");
               }
           }
         }
        }
    }
    

    ③. 同步方法

  3. ①. 同步方法:就是把synchronized 关键字加到方法上
    同步方法的锁对象是什么呢? this
    格式:修饰符 synchronized 返回值类型 方法名(方法参数){ }

    private int tickets = 100;
    private Object obj = new Object();
    private int x = 0;

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
//                synchronized (obj) {
        synchronized (this) {    
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--;
                    }
                }
            } else {
                sellTicket();
            }
            x++;
        }
    }

  private synchronized void sellTicket() {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
            tickets--;
        }
    }

}
 

  • ②. 同步静态方法:就是把synchronized关键字加到静态方法上
    格式:修饰符 static synchronized 返回值类型 方法名(方法参数){ }
    同步静态方法的锁对象是什么呢?
    类名.class

public class SellTicket implements Runnable {

    private static int tickets = 100;
    private Object obj = new Object();
    private int x = 0;

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {

                synchronized (SellTicket.class) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--;
                    }
                }
            } else {
         
                sellTicket();
            }
            x++;
        }
    }

    private static synchronized void sellTicket() {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
            tickets--;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923213.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一个高度可扩展的 Golang ORM 库【GORM】

GORM 是一个功能强大的 Golang 对象关系映射&#xff08;ORM&#xff09;库&#xff0c;它提供了简洁的接口和全面的功能&#xff0c;帮助开发者更方便地操作数据库。 1. 完整的 ORM 功能 • 支持常见的关系模型&#xff1a; • Has One&#xff08;一对一&#xff09; • …

ubuntu24挂载硬盘记录

1、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff1a; sudo fdisk -l 找到自己硬盘的分区 我的地址/dev/sda 2、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff0c;格式化自己硬盘&#xff1a; sudo mkfs -t ext4 /dev/sda 3、在终端窗口中输入如下…

Flink四大基石之Window

为什么要用WIndow 在流处理应用中&#xff0c;数据是连续不断的&#xff0c;有时我们需要做一些聚合类的处理&#xff0c;例如&#xff1a;在过去的1分钟内有多少用户点击了我们的网页。 在这种情况下&#xff0c;我们必须定义一个窗口(window)&#xff0c;用来收集最近1分钟内…

使用ENSP实现默认路由

一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为2.2.2.1/24 ip address 2.2.2.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为1.…

《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试

一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器&#xff0c;旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…

vue 预览pdf 【@sunsetglow/vue-pdf-viewer】开箱即用,无需开发

sunsetglow/vue-pdf-viewer 开箱即用的pdf插件sunsetglow/vue-pdf-viewer, vue3 版本 无需多余开发&#xff0c;操作简单&#xff0c;支持大文件 pdf 滚动加载&#xff0c;缩放&#xff0c;左侧导航&#xff0c;下载&#xff0c;页码&#xff0c;打印&#xff0c;文本复制&…

1-golang_org_x_crypto_bcrypt测试 --go开源库测试

1.实例测试 package mainimport ("fmt""golang.org/x/crypto/bcrypt" )func main() {password : []byte("mysecretpassword")hashedPassword, err : bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)if err ! nil {fmt.Println(err)…

嵌入式的C/C++:深入理解 static、const 与 volatile 的用法与特点

目录 一、static 1、static 修饰局部变量 2、 static 修饰全局变量 3、static 修饰函数 4、static 修饰类成员 5、小结 二、const 1、const 修饰普通变量 2、const 修饰指针 3、const 修饰函数参数 4. const 修饰函数返回值 5. const 修饰类成员 6. const 与 #defi…

超高流量多级缓存架构设计!

文章内容已经收录在《面试进阶之路》&#xff0c;从原理出发&#xff0c;直击面试难点&#xff0c;实现更高维度的降维打击&#xff01; 文章目录 电商-多级缓存架构设计多级缓存架构介绍多级缓存请求流程负载均衡算法的选择轮询负载均衡一致性哈希负载均衡算法选择 应用层 Ngi…

【C++ 算法进阶】算法提升二十三

目录 左右数组相减绝对值最大值 &#xff08;题意代换&#xff09;题目题目分析 可整合数组 &#xff08;题意代换&#xff09;题目题目分析代码 水王问题题目题目分析代码水王问题变形思路讲解 合并石头的最低成本 &#xff08;动态规划&#xff09;题目题目分析代码 左右数组…

solr 远程命令执行 (CVE-2019-17558)

漏洞描述 Apache Velocity是一个基于Java的模板引擎&#xff0c;它提供了一个模板语言去引用由Java代码定义的对象。Velocity是Apache基金会旗下的一个开源软件项目&#xff0c;旨在确保Web应用程序在表示层和业务逻辑层之间的隔离&#xff08;即MVC设计模式&#xff09;。 Apa…

idea怎么打开两个窗口,运行两个项目

今天在开发项目的时候&#xff0c;前端希望运行一下以前的项目&#xff0c;于是就需要开两个 idea 窗口&#xff0c;运行两个项目 这里记录一下如何设置&#xff1a;首先依次点击&#xff1a; File -> Settings -> Appearance & Behavior ->System Settings 看到如…

PPT分享 | IBM集团业务流程架构顶层规划-订单到交付-销售到回款方案

PPT下载链接见文末~ IBM业务流程规划方法是一套结构化、体系化的流程设计理论&#xff0c;其企业流程框架&#xff08;EPF&#xff09;是一种用于企业业务流程架构设计梳理的方法论。 一、IBM业务流程规划方法的核心 IBM的BPM&#xff08;业务流程管理&#xff09;流程管理体…

MySQL闪回恢复:轻松应对数据误删,数据安全有保障

在数据库管理中&#xff0c;数据误删是一个常见且棘手的问题。传统的数据恢复方法可能涉及复杂的操作&#xff0c;如全量备份和增量备份的恢复。MySQL的闪回恢复功能提供了一种更为简便、高效的数据恢复手段。本文将详细介绍MySQL闪回恢复的原理、配置和使用方法&#xff0c;帮…

加菲工具 - 好用免费的在线工具集合

加菲工具 https://orcc.online AI 工具 加菲工具 集合了目前主流的&#xff0c;免费可用的ai工具 文档处理 加菲工具 pdf转word、office与pdf互转等等工具都有链接 图片图标 加菲工具 统计了好用免费的在线工具 编码解码 加菲工具 base64编码解码、url编码解码、md5计算…

uniapp跨域问题解决方案

uniapp跨域问题解决方案 引言 在使用 uni-app 本地开发 H5> 平台时&#xff0c;需要使用浏览器进行调试&#xff0c;而浏览器会有跨域的问题。比如直接通过本地IP地址去访问开发中的页面&#xff0c;同时这个页面会调一些现有的接口时&#xff0c;就面临着跨域的问题。 解决…

ensp静态路由实验

一、实验目的 1、熟练掌握交换机的基本配置命令 2、熟练掌握静态路由的使用方法 3. 熟练掌握交换机端口模式 二、实验内容 需求&#xff1a; 根据要求利用现有实验设备组建小型局域网 实验设备&#xff1a; 交换机S37002台&#xff1b;PC机2台&#xff1b;路由器2台。 …

I2C学习

详情学习 12. I2C通讯 — [野火]Linux基础与应用开发实战指南——基于LubanCat-RK系列板卡 文档 (embedfire.com) I2C总线协议详解&#xff08;特点、通信过程、典型I2C时序&#xff09;-CSDN博客 彻底搞懂I2C总线&#xff08;一&#xff09;什么是I2C&#xff1f;什么是总线…

Neural Magic 发布 LLM Compressor:提升大模型推理效率的新工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

AIGC实战——生成式人工智能总结与展望

AIGC实战——生成式人工智能总结与展望 0. 前言1. 生成式人工智能发展历程1.1 VAE 和 GAN 时代1.2 Transformer 时代1.3 大模型时代 2. 生成式 AI 的当前进展2.1 大语言模型2.2 文本生成代码模型2.3 文本生成图像模型2.4 其他应用 3. 生成式人工智能发展展望3.1 生成式 AI 在工…