【JAVA CORE_API】Day19 多线程API(2)、多线程并发安全问题、同步

news2024/11/26 12:33:31

多线程API

进程和线程

  • 进程:进程就像是一个程序在电脑里运行时的一个实例。你可以把它想象成一个独立的小工人,专门负责完成某项任务(比如打开浏览器、播放音乐)。每个进程都有自己独立的资源(比如内存)和工作空间,不会轻易打扰其他进程。多个进程可以同时运行,各自处理自己的任务。

    在这里插入图片描述

  • 线程:线程就像是一个进程中的小工人。一个进程可以有多个线程,每个线程负责进程中的某个具体工作,比如在一个浏览器里同时加载不同的网页。线程之间可以共享进程的资源(比如内存),所以它们可以更快地协同工作。但因为它们共享资源,也需要小心不要互相干扰。

    在这里插入图片描述

进程和线程的关系

  • 进程和线程的关系就像是公司和员工的关系。一个进程是公司,它可以有多个员工(线程)一起工作。每个员工都有自己的任务,但他们共享公司的资源(比如办公设备)。

  • 进程切换是指CPU从一个公司(进程)跳到另一个公司,执行不同公司的任务。因为每个公司有自己的资源,所以这个切换需要花费时间去保存和加载这些资源。

  • 线程切换是指CPU在同一个公司内换员工执行任务。因为员工共享公司的资源,切换时不需要太多额外操作,所以速度更快。
    - CPU通过快速切换进程和线程来处理多任务,确保每个任务都能得到执行。

进程与线程的区别

  • 资源占用

    • 进程:进程是独立的,拥有自己完整的资源集,包括内存、文件句柄等。每个进程有自己独立的地址空间,这使得进程之间相对隔离,互不干扰。

    • 线程:线程是进程内的一个执行单元,多个线程共享同一个进程的资源(如内存、文件句柄)。因此,线程之间的资源共享更直接、效率更高,但也更容易引发资源争用的问题。

    在这里插入图片描述

  • 开销

    • 进程:进程的创建、销毁和切换都需要较大的开销。因为进程切换涉及到保存和恢复独立的资源(如内存状态、CPU寄存器等),所以会消耗更多的系统资源和时间。

    • 线程:线程的创建、销毁和切换开销较小。因为线程共享进程的资源,切换时只需保存和恢复少量的线程上下文,所以切换速度更快,系统资源占用更少。

  • 并发性

    • 进程:进程之间的并发性较弱,因为它们彼此独立,不容易直接共享数据。要实现进程间的通信(IPC),通常需要借助系统提供的机制(如管道、消息队列等),这会增加复杂性和开销。

    • 线程:线程之间的并发性较强。由于线程共享进程的资源,多个线程可以更容易地协同工作、直接共享数据,从而提高并发执行的效率。但同时,这也容易引发线程间的同步问题,如数据竞争和死锁。

线程的生命周期

  1. 新建(New):线程对象被创建,但还没有开始执行。就像一个人刚出生,刚有了生命,但还没有真正开始行动。

  2. 就绪(Runnable):线程已经准备好执行,等待CPU调度。就像人长大到可以学习和工作的年龄,准备好去做事,等待机会开始行动。

  3. 运行(Running):线程获得CPU时间片,开始执行任务。就像当人获得工作机会或任务,开始真正行动,比如开始学习、工作。

  4. 阻塞(Blocked):线程因等待某些资源(如锁、IO)而暂停执行,直到资源可用。就像人遇到困难或等待某些条件(比如等材料、等别人回应),暂时不能继续工作,只能等待问题解决。

  5. 终止(Terminated):线程执行完毕或因异常退出,生命周期结束。就像人完成任务或退休,生命结束,所有活动停止。

    在这里插入图片描述

线程优先级

  • 线程优先级是一个数值,用来决定线程获取CPU时间的先后顺序。优先级越高,线程越有可能先被执行。

    • 线程切换方式:操作系统根据线程优先级和调度算法,在多个线程间切换,分配CPU资源。

    • 设置线程优先级:通过setPriority(int newPriority)方法设置,优先级范围从1(最低)到10(最高)。

    • 默认优先级:所有线程默认优先级为5,即中等优先级。

  • 优先级只是一个建议,具体调度由操作系统决定。

  • Java 中有三个线程优先级常量,用于方便地设置线程的优先级:

    1. Thread.MIN_PRIORITY:最小优先级,值为1。适用于不紧急的任务。

    2. Thread.NORM_PRIORITY:普通优先级,值为5。是默认优先级,适用于一般任务。

    3. Thread.MAX_PRIORITY:最大优先级,值为10。适用于需要优先处理的任务。

 package day19;
 
 /**
  * 线程的优先级
  * 线程有10个优先级,分别用整数1-10表示,其中1是最低的,10是最高的,5是默认值
  */
 public class PriorityDemo {
     public static void main(String[] args) {
         Thread thread01 = new Thread(){
             @Override
             public void run() {
                 for (int i = 0; i < 1000; i++) {
                     System.out.println("Min" + i);
                 }
             }
         };
 
         Thread thread02 = new Thread(){
             @Override
             public void run() {
                 for (int i = 0; i < 1000; i++) {
                     System.out.println("Normal:" + i);
                 }
             }
         };
 
         Thread thread03 = new Thread(){
             @Override
             public void run() {
                 for (int i = 0; i < 1000; i++) {
                     System.out.println("Max:" + i);
                 }
             }
         };
 
         thread01.setPriority(Thread.MIN_PRIORITY);
         thread02.setPriority(Thread.NORM_PRIORITY);
         thread03.setPriority(Thread.MAX_PRIORITY);
 
         thread01.start();
         thread02.start();
         thread03.start();
     }
 }
 

sleep阻塞

sleep阻塞是指线程调用Thread.sleep(milliseconds)方法后,进入阻塞状态,暂停执行指定的时间。

  • 作用sleep方法用于让当前线程暂停执行,释放CPU,让其他线程有机会运行。

  • 特点:线程在sleep期间保持着它的资源(如锁),只是暂时停止运行。当时间到达后,线程自动恢复到就绪状态,等待CPU再次调度。

  • 注意sleep不会释放锁资源,也不会影响线程的优先级。它只是简单地让线程暂停一段时间。

    sleep阻塞常用于控制线程执行的节奏,比如定时任务或轮询操作。

     package day19;
     
     /**
      * Thread提供了一个方法:static void sleep(long ms)
      * 暂停当前线程,让出CPU,让出时间片,等待ms毫秒
      * 超时后会自动回到Runnable状态再次开始并发
      */
     public class SleepDemo {
         public static void main(String[] args) {
             System.out.println("程序开始...");
             try {
                 Thread.sleep(5000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println("程序结束了...");
         }
     }
     
    
    • 在实际使用情况下,sleep()有以下几种常见的用途:

      • 控制任务执行频率:使用 sleep() 来暂停线程,使其每隔一定时间执行任务。例如,定时检查某个条件或定期更新状态。

      • 模拟延迟:在开发和测试过程中,使用 sleep() 模拟网络延迟或处理时间,以测试系统对延迟的反应。

      • 避免资源过度占用:在高频率的任务(如轮询)中,使用 sleep() 减少对CPU的过度占用,降低系统负载。

      • 实现简单的等待机制:在多线程程序中,线程可能需要等待某些条件满足后才能继续执行。使用 sleep() 可以实现简单的等待或延时。

      • 调试和测试:在调试和测试中,sleep() 可用于创建特定的时间间隔,以便观察程序的行为或结果。

    • 我们来写一个定时器Demo,需求:程序运行后在控制台上输入一个数字,然后从该数字开始每秒递减,到0时输出时见到。

       package day19;
       
       import java.util.Scanner;
       
       public class ClockDemo {
           public static void main(String[] args) {
               System.out.println("请输入一个数字:");
               Scanner scanner = new Scanner(System.in);
               System.out.println("倒计时开始!");
               for (int i = scanner.nextInt(); i >= 0; i--) {
                   System.out.println(i);
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               System.out.println("时间到!");
           }
       }
       
      

InterruptedExcption阻塞中断异常

  • InterruptedException 是 Java 中的一个异常,表示线程在阻塞状态(如 sleep()wait()join())时被中断了。

  • sleep() 方法中的作用:

    • 当线程调用 sleep() 方法进入阻塞状态时,如果在等待期间有其他线程调用 interrupt() 方法中断这个线程,sleep() 方法会抛出 InterruptedException

    • 这允许线程在被中断时可以立即恢复执行,并处理中断请求。

  • 使用场景:

    1. 响应中断请求:在长时间运行的线程中,使用 sleep() 时可能会有其他线程请求中断。此时,InterruptedException 让线程能够响应中断请求,做出适当的处理(如清理资源、退出循环)。

    2. 中断支持的任务:在需要支持中断的任务中,比如一个线程正在等待或处理任务,但也需要能够中断以快速终止任务或恢复其他操作,使用 sleep() 时应考虑 InterruptedException

  • 实际开发中,处理 InterruptedException 是重要的,特别是在多线程程序中,确保线程能够响应中断请求并适当地退出或清理资源。

    • 示例:

       package day19;
       
       /**
        * 线程阻塞打断异常
        */
       public class InterruptedExceptionDemo {
           public static void main(String[] args) {
               Thread thread = new Thread("美女"){
                   @Override
                   public void run() {
                       try {
                           System.out.println(getName() + ":刚美完容,睡一觉吧...zZ");
                           Thread.sleep(100000000);
                       } catch (InterruptedException e) {
                           System.out.println(getName() + ":干嘛呢?干嘛呢!干嘛呢!!!");
                       }
                   }
               };
       
               Thread thread1 = new Thread("装修工") {
                   @Override
                   public void run() {
                       System.out.println(getName() + ":大锤80!小锤40!大哥你要几锤?");
                       for (int i = 0; i < 5; i++) {
                           System.out.println(getName() + ":80!");
                           try {
                               Thread.sleep(1000);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                       }
                       System.out.println("咣当!");
                       System.out.println(getName() + ":大哥!搞定!!!");
                       thread.interrupt();
                   }
               };
               thread.start();
               thread1.start();
           }
       }
       
      

守护线程

  • 什么是守护线程

    守护线程(Daemon Thread)是指在后台运行的线程,它为其他线程提供服务,但不会阻止程序退出。守护线程通常用于执行不重要的任务,如垃圾回收、后台监控等。

  • 守护线程和普通线程的区别

    1. 生命周期

      • 守护线程:当所有用户线程(非守护线程)全部结束时,守护线程才会结束。它不会阻止程序退出。

      • 普通线程:程序的退出会等到所有普通线程都结束后,程序才会终止。

    2. 优先级

      • 守护线程:通常优先级较低,因为它的任务是辅助性质的。

      • 普通线程:优先级可以根据任务的重要性设定。

  • 如何使用守护线程

    在创建线程时,可以通过 Thread 类的 setDaemon(true) 方法将其设为守护线程。例如:

     void setDeamon(boolean)   // 当参数为true时该线程为守护线程
    
  • 守护线程的特点

    1. 后台服务:守护线程通常用于后台服务任务,如垃圾回收、日志记录、监控等。

    2. 自动退出:守护线程不会阻止程序终止,当所有非守护线程结束时,守护线程也会结束。

  • 守护线程与垃圾回收(GC)

    守护线程通常用于执行垃圾回收任务。Java 的垃圾回收线程就是一种守护线程,它在后台运行,清理不再使用的对象,而不会阻止程序的终止。

  • 守护线程在什么情况使用

    • 后台任务:适用于需要在后台持续运行的任务,而这些任务不应该阻止程序退出。例如,后台监控、定时任务、后台日志记录等。

    • 辅助服务:适用于提供服务或支持其他用户线程的任务,比如线程池中的工作线程通常是守护线程。

 package day19;
 
 public class DaemonThreadDemo {
     public static void main(String[] args) {
         // Rose线程
         Thread threadRose = new Thread("Rose"){
             @Override
             public void run() {
                 for (int i = 0; i < 5; i++) {
                     System.out.println(getName() + ":Let me go!!!");
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
                 System.out.println(getName() + ":AAAAAAAAAA!!!!");
                 System.out.println("噗通~");
             }
         };
         
         // Jack线程
         Thread threadJack = new Thread("Jack"){
             @Override
             public void run() {
                 while (true) {
                     System.out.println(getName() + ":You jump! I jump!");
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
         };
       
         // 线程控制
         threadRose.start();
         threadJack.setDaemon(true);  // 将threadJack设置为守护线程
         threadJack.start();
     }
 }
 

多线程

多线程并发安全

  • 生活中的例子

    • 共享银行账户

      • 场景:假设有一个银行账户,两个不同的客户同时使用自动取款机(ATM)和柜台取款同一账户,每个客户就是一个线程,如果CPU执行客户1取款线程的一半时间片结束将线程分配给客户2,检查余额时恰巧同时进入检查余额是否足够的阶段,会不会出现都返回true的情况发生?两个人都成功取款?我们看下面的代码:

         package day19;
         
         /**
          * 当多个线程并发操作同一临界资源,由于线程切换实际不确定,导致执行顺序出现混乱而产生不良后果,这就是线程并发安全问题
          *
          * 临界资源:操作该资源的过程同时只能被单个线程进行
          */
         public class SyncDemo01 {
             static boolean success1 = false;  // 表示第一个人是否取款成功
             static boolean success2 = false;  // 表示第二个人是否取款成功
         
             public static void main(String[] args) {
                 int sum = 0;  // 记录测试的次数
                 Bank bank = new Bank();
                 while (true) {
                     sum++;
                     Thread thread1 = new Thread() {
                         @Override
                         public void run() {
                             success1 = bank.getMoney(20000);  // 取款
                         }
                     };
                     Thread thread2 = new Thread() {
                         @Override
                         public void run() {
                             success2 = bank.getMoney(20000);  // 取款
                         }
                     };
                     thread1.start();
                     thread2.start();
         
                     try {
                         Thread.sleep(10);  // 阻塞主线程一段时间,用于让t1,t2线程执行完毕
                         if (success1 && success2) {
                             System.out.println("成功!两个人都取出20000块!");
                             break;
                         } else {
                             System.out.println("失败!正常取款,只有一人取款成功!");
                             success1 = false;
                             success2 = false;
                             bank.saveAccount(20000);
                         }
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println("第" + sum + "次测试:");
                 }
             }
         }
         
         class Bank {
             private int account = 20000;  // 银行余额
         
             // 取钱方法,返回true说明允许出钞,返回false说明不允许出钞
             public boolean getMoney(int money) {
                 int account = getAccount();  // 查询余额方法
                 if (account >= money) {  // 判断余额是否足够
                     account = account - money;  // 扣款
                     Thread.yield();  // 主动放弃当前线程的时间片,模拟到这里CPU恰好分配的时间片结束
                     saveAccount(account);  // 保存新余额
                     return true;  // 允许出钞
                 }
                 return false;  // 不允许出钞
             }
         
             public void saveAccount(int account){
                 this.account = account;
             }
         
             public int getAccount() {
                 return account;
             }
         }
         
        
      • 并发安全:为了防止两个客户取款同时修改账户余额导致不一致,银行系统需要使用机制(如锁)来确保每次只有一个客户能够修改账户余额,从而保证账户余额的准确性。

  • 计算机相关的例子

    • 线程安全的计数器

      • 场景:多个线程同时增加一个全局计数器的值。如果没有适当的同步机制,多个线程同时对计数器进行操作可能会导致计数器值的丢失或重复增加。

      • 并发安全:为了确保计数器的正确性,通常会使用线程安全的数据结构(如 AtomicInteger)或在增加计数器时使用同步机制(如 synchronized 关键字或 Lock)来保证每次操作的原子性,从而避免数据竞争。

如何解决并发问题

  • 同步(Synchronization)

    同步在多线程编程中是指协调多个线程对共享资源的访问,以防止并发操作导致数据的不一致性。由于多个线程可能同时访问和修改共享数据,如果没有同步机制,就会导致数据竞争和不可预测的结果。

  • synchronized 关键字

    Java 提供了 synchronized 关键字来实现同步,确保同一时刻只有一个线程可以执行被 synchronized 修饰的代码部分,从而保护共享资源。

  • synchronized 的两种用法:

    • 同步方法

      • 当一个方法被 synchronized 修饰时,线程在调用这个方法之前需要获得方法所属对象的锁(如果是静态方法,则是类对象的锁)。

      • 当一个线程持有对象锁时,其他线程必须等待,直到这个锁被释放后才能访问这个同步方法。

      public synchronized void exampleMethod() {
          // 同步代码
      }
      
    • 同步代码块

      • 通过 synchronized 关键字可以对一个特定的对象加锁,通常是 this,以确保某一代码块在同一时间只能被一个线程执行。

      • 这种方式比同步整个方法更灵活,只锁住关键的代码部分,可能减少不必要的性能损失。

       public void exampleMethod() {
           synchronized(this) {
               // 同步代码块
           }
       }
      
  • 优化刚才的代码:

     package day19;
     
     /**
      * 当多个线程并发操作同一临界资源,由于线程切换实际不确定,导致执行顺序出现混乱而产生不良后果,这就是线程并发安全问题
      *
      * 临界资源:操作该资源的过程同时只能被单个线程进行
      */
     public class SyncDemo01 {
         static boolean success1 = false;  // 表示第一个人是否取款成功
         static boolean success2 = false;  // 表示第二个人是否取款成功
     
         public static void main(String[] args) {
             int sum = 0;  // 记录测试的次数
             Bank bank = new Bank();
             while (true) {
                 sum++;
                 Thread thread1 = new Thread() {
                     @Override
                     public void run() {
                         success1 = bank.getMoney(20000);  // 取款
                     }
                 };
                 Thread thread2 = new Thread() {
                     @Override
                     public void run() {
                         success2 = bank.getMoney(20000);  // 取款
                     }
                 };
                 thread1.start();
                 thread2.start();
     
                 try {
                     Thread.sleep(10);  // 阻塞主线程一段时间,用于让t1,t2线程执行完毕
                     if (success1 && success2) {
                         System.out.println("成功!两个人都取出20000块!");
                         break;
                     } else {
                         System.out.println("失败!正常取款,只有一人取款成功!");
                         success1 = false;
                         success2 = false;
                         bank.saveAccount(20000);
                     }
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 System.out.println("第" + sum + "次测试:");
             }
         }
     }
     
     class Bank {
         private int account = 20000;  // 银行余额
     
         // 取钱方法,返回true说明允许出钞,返回false说明不允许出钞
         public synchronized boolean getMoney(int money) {
             int account = getAccount();  // 查询余额方法
             if (account >= money) {  // 判断余额是否足够
                 account = account - money;  // 扣款
                 Thread.yield();  // 主动放弃当前线程的时间片,模拟到这里CPU恰好分配的时间片结束
                 saveAccount(account);  // 保存新余额
                 return true;  // 允许出钞
             }
             return false;  // 不允许出钞
         }
     
         public void saveAccount(int account){
             this.account = account;
         }
     
         public int getAccount() {
             return account;
         }
     }
     
    
  • 这段代码还存在性能问题:原因是把 synchronized 锁放在了整个 getMoney() 方法上。虽然这样可以解决线程并发安全问题,但会导致性能下降,因为它限制了同一时间只有一个线程能够访问整个方法,这在多线程环境中会降低系统的吞吐量。

  • 生活中的类比:去商场买衣服,想象你去商场买衣服的过程:

    • 全程锁定:如果商场为了避免拥挤,每次只允许一个顾客进入并从头到尾完成所有购物步骤(挑选、试穿、付款等),这样可以确保秩序,但效率非常低,其他顾客必须等待很长时间才能轮到自己。

    • 部分锁定:更合理的做法是只在关键步骤上(比如付款时)进行控制,同一时间只允许一个顾客在收银台结账,而其他顾客可以同时进行挑选和试穿。这样可以大大提高整体效率。

  • 如何解决锁带来的性能问题呢?

同步块

  • 同步块的组成和语法

    同步块synchronized 关键字和一个同步监视器对象(锁)组成,确保在同一时间只有一个线程可以执行同步块内的代码。

  • 语法:

     java
     复制代码
     synchronized(锁对象) {
         // 需要同步的代码
     }
    
  • 同步监视器对象的选取

    • 锁对象是同步块的关键,它用于控制访问权限。

    • 常见选择

      • this:当前对象实例,用于实例方法的同步。

      • 类对象ClassName.class,用于静态方法的同步。

      • 任意其他对象:用于细粒度控制,避免锁定过多代码。

     package day19;
     
     public class SyncDemo02 {
         public static void main(String[] args) {
             shop shop = new shop();
             Thread thread01 = new Thread("张禹垚一号") {
                 @Override
                 public void run() {
                     shop.buy();
                 }
             };
     
             Thread thread02 = new Thread("张禹垚二号") {
                 @Override
                 public void run() {
                     shop.buy();
                 }
             };
     
             thread01.start();
             thread02.start();
         }
     }
     
     class shop {
         public void buy(){
             try {
                 Thread thread = Thread.currentThread();  // 获取当前线程对象
                 System.out.println(thread.getName() + ":正在购买商品...");
                 Thread.sleep(5000);
     
                 synchronized (this) {
                     System.out.println(thread.getName() + ":正在试衣服...");
                     Thread.sleep(5000);
                 }
     
                 System.out.println(thread.getName() + ":购买成功!");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
    
  • 选择合适的锁:

    • 锁粒度:锁的范围应尽量小,以减少线程争用。选择只需要保护共享资源的关键部分作为锁对象。

    • 实例锁:用 this 作为锁对象,适用于保护当前实例的共享资源,确保同一实例的同步方法或代码块同时只能被一个线程访问。

    • 类锁:用 ClassName.class 作为锁对象,适用于保护静态变量或方法,确保同类的所有实例共享同一把锁。

    • 自定义锁:用特定的对象作为锁,适用于需要更灵活的同步控制,避免过多的竞争。选择与共享资源最相关的对象。

    • 一致性:确保所有相关代码段使用同一个锁对象,防止线程间产生不一致的锁定。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python 可迭代,迭代器,生成器,装饰器

1. 可迭代&#xff08;Iterable&#xff09; 可迭代 是指一个对象可以返回一个迭代器的对象。也就是说&#xff0c;它实现了 __iter__() 方法或 __getitem__() 方法。常见的可迭代对象有列表、元组、字符串、字典和集合。 from collections.abc import Iterablei 100 s &qu…

墙裂推荐!云上机密计算,阿里云上体验了一下海光内存加密和远程认证

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 机密计算目录 前言1、构…

哈希原理实现

本节主要看源代码实现 哈希特点 哈希&#xff08;Hashing&#xff09;是一种将数据映射到固定大小的表中以实现快速查找的数据结构和算法方法。哈希的主要特点包括&#xff1a; 1. 高效的查找、插入和删除 时间复杂度&#xff1a;哈希表通常提供近乎常数时间的查找、插入和…

app安全评估报告的常见留存措施(内附独家资料)

对用户账号、操作时间、操作类型、网络源地址和目标地址、网络源端口、客户端硬件特征等日志信息以及用户发布信息记录的留存措施 1**. 用户账号信息**&#xff1a;我们将用户账号信息安全存储&#xff0c;只有授权的人员能够访问。这些信息包括用户名、电子邮件地址等&#xf…

【C++ 面试 - 面向对象】每日 3 题(六)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

VAuditDemo审计之二次注入漏洞

目录 VAuditDemo二次注入漏洞 搜索危险函数&#xff0c;用户可控点 regCheck.php messageSub.php message.php 漏洞调用链 漏洞错误利用过程 注册用户 xxxx, 发表payload留言 漏洞正确利用过程 注册用户 wwww\ 退出用户 wwww\\ 使用 wwww\ 登录 发表留言 替换dat…

《javaEE篇》--定时器

定时器概念 当我们不需要某个线程立刻执行&#xff0c;而是在指定时间点或指定时间段之后执行&#xff0c;假如我们要定期清理数据库里的一些信息时&#xff0c;如果每次都手动清理的话就太麻烦&#xff0c;所以就可以使用定时器。定时器就可以比作一个闹钟&#xff0c;可以让…

C++ 设计模式(6. 适配器模式)

适配器模式Adapter Pattern是一种结构型设计模式&#xff0c;它可以将一个类的接口转换成客户希望的另一个接口&#xff0c;主要目的是充当两个不同接口之间的桥梁&#xff0c;使得原本接口不兼容的类能够一起工作。基本结构 Target 是目标接口&#xff0c;Adaptee 是需要适配的…

微信小程序实例代码解读

以微信 小程序开发工具给的示例代码为例&#xff1a; 主页代码&#xff1a; index.wxml 这个文件是一个微信小程序页面的 WXML 结构,主要功能是展示一个快速开始教程的步骤和内容。 源代码&#xff1a; <!--index.wxml--> <view class"container">&l…

ZK-Rollups测评

1. 引言 Matter Labs团队和多个高校研究人员一起&#xff0c;发布2024年论文《Analyzing and Benchmarking ZK-Rollups》&#xff0c;开源代码见&#xff1a; https://github.com/StefanosChaliasos/zkrollup-benchmarking&#xff08;Python&#xff09; 其中&#xff1a; …

安装MySQL入门基础指令

一.安装MySQL(以5.7版本为例) 1.一路默认安装&#xff0c;截图供大家参考 修改自己window安装名字即可 2.配置环境变量 C:\Program Files\MySQL\MySQL Server 5.7\bin 写入系统环境变量即可在window窗口使用其服务了 3.登录MySQL服务 进入控制台输入命令 mysql -u root …

运维小技能:基于Windows系统和‌Linux系统,以tomcat为案例,讲解如何新增自启动服务。

文章目录 引言‌I Linux系统‌(以CentOS为例)基础知识:运行级别(run level)基于chkconfig 工具,设置服务启动类型。基于systemctl 新增系统服务II 基于Windows系统设置服务自启动的常规操作安装多个tomcat服务,并设置自启动。III 扩展制定定时任务优化停止Tomcat服务命令引…

ESP32Cam人工智能教学20

ESP32Cam人工智能教学20 ESP32Cam专用APP 这次我们专门为ESP32Cam量身定制一个手机APP。手机APP是客户端&#xff0c;利用Socket连接ESP32Cam&#xff0c;ESP32Cam成了服务器&#xff0c;实现Socket全双工的数据传输模式&#xff0c;还可以一边显示摄像头图像&#xff0c;一边…

【Canvas与诗词】北岛诗《献给遇罗克》节选(以太阳的名义...)

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>以太阳的名义</title><style type"text/css">…

基类没有虚析构,即使派生类使用智能指针也一定会内存泄漏

实验 定义一个基类和一个派生类 class Base { public:virtual ~Base() default; };class Derive :public Base { public:std::shared_ptr<int> sp{new int{0},[](int *p){delete p;std::cout << "删除器" << endl;},}; };main 函数执行如下代码…

作业08.21

服务器&#xff1a; #include <myhead.h>#define SER_PORT 6666 #define SER_IP "127.0.0.1"int find_client(int *client_arr, int len, int client) {for(int i0; i<len; i){if(client_arr[i] client){return i;}}return -1; }void remove_client(int *…

Mac 使用vscode 创建vue项目后修改文件提示:权限不足,以超级用户身份重试

项目场景&#xff1a; Mac 安装了全局 vue-cli 插件后&#xff0c;使用webpack 创建vue项目&#xff0c;打开项目&#xff0c;选择信任所有文件夹&#xff0c;然后正常编写代码&#xff0c;并对项目中的文件进行修改&#xff0c;点击保存的时候提示&#xff1a;保存“webpack.…

Vue3+Ts封装类似el-dialog的对话框组件

提供11个字段对dialog组件进行控制&#xff1a; modelValue: 对话框显示隐藏控制, width: 控制对话框的宽度, height&#xff1a;控制对话框的高度, top: 控制对话框个距离顶部的距离, title: 控制对话框的标题, appendToBody: 是否将对话框添加至body, closeOnClickModa…

GX Works2的使用方法

目录&#xff1a; 1、概述 2、硬件连接 3、录入与修改程序 1&#xff09;进入编辑按F2或点击“写入模式”图标 2&#xff09;修改部分元件 3&#xff09;注释 4&#xff09;改变显示触点数 4、软仿真与在线仿真 1&#xff09;软仿真 2&#xff09;在线仿真 5、P…

Linux源码阅读笔记-USB设备驱动架构

总线速度及主机控制器 USB系统架构 USB系统主机端提供为4个引脚的A型接口&#xff0c;USB外围设备通过4个引脚的B型接口和主机端连接。那4个引脚&#xff08;一条电压线VBUS、一条地线GND、一条正方向传输数据的D和一条反方向传输数据的D-线。&#xff09;USB主机和USB设备收发…