第六章:多线程

news2025/7/11 17:00:06

第六章:多线程

6.1:程序、进程、线程基本概念

  1. 程序

    程序program是为了完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

  2. 进程

    ​ 进程process是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。【生命周期】

    • 程序是静态的,进程是动态的。
    • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。
  3. 线程

    进程可进一步细化为线程,是一个程序内部的一条执行路径。

    • 若一个进行同一时间并行执行多个线程,就是支持多线程的。
    • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
    • 一个进程中的多个线程共享相同的内存单元/内存地址空间。他们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但是多个线程操作共享的系统资源可能就会带来安全的隐患
  4. 单核CPU和多核CPU的理解

    • 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。
    • 如果是多核的话,才能更好的发挥多线程的效率。
    • 一个Java应用程序java.exe,其实至少有三个线程。main()主线程gc()垃圾回收线程异常处理线程
  5. 并行与并发

    • 并行:多个CPU同时执行多个任务。

    • 并发:一个CPU(采用时间片)同时执行多个任务。

  6. 使用多线程的优点

    • 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
    • 提供计算机系统CPU的利用率。
    • 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
  7. 何时需要多线程

    • 程序需要同时执行两个或多个任务。
    • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
    • 需要一些后台运行的程序时。

6.2:线程的创建和使用

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。

  1. Thread类的特性

    • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。
    • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()
  2. Thread类的构造器

    • Thread():创建新的Thread对象
    • Thread(String threadname):创建线程并指定线程实例名。
    • Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法。
    • Thread(Runnable target, String name):创建新的Thread对象。
  3. 多线程的创建一:继承Thread

    • 创建一个继承与Thread类的子类。
    • 重写Thread类的run() ----> 将此线程执行的操作声明在run()中。
    • 创建Thread类的子类的对象。
    • 通过此对象调用start()
    // 1.创建一个继承与Thread类的子类。
    class MyThread extends Thread{
    	// 2.重写Thread类的run()  
        public void run() {
            for(int i = 0; i < 100; i++) {
                if(i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args) {
            // 1.创建Thread类的子类的对象
            MyThread t1 = new MyThread();
            // 2.通过此对象调用start()
            t1.start();
        }
    }
    
  4. 多线程的创建二:实现Runnable接口

    • 创建一个实现了Runnable接口的类。
    • 实现类去实现Runnable中的抽象方法:run()
    • 创建实现类的对象。
    • 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
    • 通过Thread类的对象调用start()
    // 1.创建一个实现了Runnable接口的类
    class MThread implements Runnable {
        // 2.实现类去实现`Runnable`中的抽象方法:run()
        public void run() {
            for(int i = 0; i < 100; i++) {
                if(i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }
    }
    
    public class ThreadTest1 {
        public static void main(String[] args) {
            // 3.创建实现类的对象
            MThread mThread = new MThread();
            // 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
            Thread t1 = new Thread(mThread);
            // 5.通过Thread类的对象调用start()
            t1.start();
        }
    }
    
  5. 比较创建线程的两种方式

    • 开发中:优先选择实现Runnable接口的方式。
    • 原因:
      1. 实现的方式没有类的单继承性的局限性。
      2. 实现的方式更适合来处理多个线程有共享数据的情况。
    • 联系:public class Thread implememts Runnable
    • 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
  6. Thread中的常用方法

    • start():启动当前线程,调用当前线程的run()
    • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
    • currentThread():静态方法,返回执行当前代码的线程。
    • getName():获取当前线程的名字。
    • setName():设置当前线程的名字。
    • yield():释放当前cpu执行权。
    • join():在线程a中调用线程bjoin(),此时线程a就进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。
    • stop():已过时。当执行此方法时,强制结束当前线程。
    • isAlive():判断当前线程是否存活。
  7. 线程的优先级

    MAX_PRIORITY: 10
    MIN_PRIORITY: 1
    NORM_PRIORITY: 5 --> 默认优先级
    
    • 如何获取和设置当前线程的优先级

      1. getPriority():获取线程的优先级。
      2. setPriority(int p):设置线程的优先级。
    • 说明

      ​ 高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

  8. 线程的分类

    Java中的线程分为两类:一种是守护线程,一种是用户线程。

    • 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
    • 守护是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
    • Java垃圾回收就是一个典型的守护线程。
    • JVM中都是守护线程,当前JVM将退出。

6.3:线程的生命周期

  1. JDK中用Thread.State类定义了线程的几种状态

    ​ 想要实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态。

    • 新建

      当一个Thread或其子类的对象被声明并创建时,新生的线程对象处于新建状态。

    • 就绪

      ​ 处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源。

    • 运行

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

    • 阻塞

      在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。

    • 死亡

      线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。

  2. 线程的生命周期图解
    在这里插入图片描述

6.4:线程的同步

  1. 多线程出现了安全问题

    • 问题的原因

      ​ 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

    • 解决办法

      ​ 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。Java对于多线程的安全问题提供了专业的解决方式:同步机制

  2. 方式一:同步代码块

    • 格式

      synchronized(同步监视器) {
      	// 需要被同步的代码
      }
      
    • 操作共享数据的代码,即为需要被同步的代码。

    • 共享数据:多个线程共同操作的变量。

    • 同步监视器【俗称:锁】。任何一个类的对象,都可以充当锁。多个线程必须要共用同一把锁。

    • 解决实现Runnable接口方式的线程安全问题

      class Window1 implements Runnable {
          private int ticket = 100;
      
          public void run() {
              while(true) {
                  synchronized (this) {
                      if (ticket > 0) {
                          try {
                              Thread.sleep(100);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
      
                          System.out.println(getName() + ":买票,票号为:" + ticket);
                          ticket--;
                      } else {
                          break;
                      }
                  }
              }
          }
      }
      
      public class WindowTest1 {
          public static void main(String[] args) {
              Window1 w = new Window1();
      
              Thread t1 = new Thread(w);
              Thread t2 = new Thread(w);
              Thread t3 = new Thread(w);
      
              t1.setName("窗口一");
              t2.setName("窗口二");
              t3.setName("窗口三");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      

      在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

    • 解决继承Thread类方式的线程安全问题

      class Window2 extends Thread {
          private static int ticket = 100;
      
          public void run() {
              while(true) {
                  synchronized(Window2.class) {
                      if(ticket > 0) {
                          try{
                              Thread.sleep(100);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
      
                          System.out.println(getName() + ":买票,票号为:" + ticket);
                          ticket--;
                      } else {
                          break;
                      }
                  }
              }
          }
      }
      
      public class WindowTest2 {
          public static void main(String[] args) {
              Window2 t1 = new Window2();
              Window2 t2 = new Window2();
              Window2 t3 = new Window2();
      
              t1.setName("窗口一");
              t2.setName("窗口二");
              t3.setName("窗口三");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      

      在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

  3. 方式二:同步方法

    • 格式

      权限修饰符 synchronized 返回值类型 方法名() {}
      

      如果操作共享数据的代码完整的声明在以方法中,我们不妨将此方法声明为同步的。

    • 同步方法仍然设计到同步监视,只是不需要我们显式的声明。

    • 非静态的同步方法,同步监视器是this。静态的同步方式,同步监视器是当前类本身。

    • 解决实现Runnable接口方式的线程安全问题

      class Window3 implements Runnable {
          private int ticket = 100;
          public void run() {
              while(true) {
                  show();
              }
          }
          
          private synchronized void show() {
                  if (ticket > 0) {
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
      
                      System.out.println(getName() + ":买票,票号为:" + ticket);
                      ticket--;
                  }
          }
      }
      
      public class WindowTest3 {
          public static void main(String[] args) {
              Window3 w = new Window3();
      
              Thread t1 = new Thread(w);
              Thread t2 = new Thread(w);
              Thread t3 = new Thread(w);
      
              t1.setName("窗口1");
              t2.setName("窗口2");
              t3.setName("窗口3");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      
    • 解决继承Thread类方式的线程安全问题

      class Window4 extends Thread {
          private static int ticket = 100;
          public void run() {
              while(true) {
                  show();
              }
          }
      
          public static synchronized void show() {
              if(ticket > 0) {
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                  ticket--;
              }
          }
      }
      
      public class WindowTest4 {
          public static void main(String[] args) {
              Window4 t1 = new Window4();
              Window4 t2 = new Window4();
              Window4 t3 = new Window4();
      
              t1.setName("窗口1");
              t2.setName("窗口2");
              t3.setName("窗口3");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      
  4. 方式三Lock(锁)

    • JDK 5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当。
    • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
    • ReentrantLock类实现了Lock,它拥有synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁。
    class Window implements Runnable {
        private int ticket = 100;
        // 1.实例化ReentrantLock
        private ReentrantLock lock = new ReentrantLock();
    
        public void run() {
            while(true) {
                try {
                    // 2.调用锁定方法lock()
                    lock.lock();
    
                    if(ticket > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        System.out.println(getName() + ":售票,票号为:" + ticket);
                        ticket--;
                    } else {
                        break;
                    }
                } finally {
                    // 3.调用解锁方法:unlock()
                    lock.unlock();
                }
            }
        }
    }
    
    public class LockTest {
        public static void main(String[] args) {
            Window w = new Window();
    
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
    
            t1.setName("窗口一");
            t2.setName("窗口二");
            t3.setName("窗口三");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
  5. synchronizedLock的异同

    相同点:二者都可以解决线程安全问题。

    不同点:

    • synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器。
    • Lock需要手动的启动同步lock(),同时结束也需要手动的实现unlock()
  6. 线程死锁

    不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

    public class ThreadTest {
        public static void main(String[] args) {
            StringBuffer s1 = new StringBuffer();
            StringBuffer s2 = new StringBuffer();
    
            new Thread() {
                public void run() {
                    synchronized(s1) {
                        s1.append("a");
                        s2.append("1");
    
                        try{
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        synchronized (s2) {
                            s1.append("b");
                            s2.append("2");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }.start();
    
            new Thread(new Runnable() {
                public void run() {
                    synchronized (s2) {
                        s1.append("c");
                        s2.append("3");
    
                        try{
                            Thread.sleep(100);
                        }catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        synchronized (s1) {
                            s1.append("d");
                            s2.append("4");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }).start();
        }
    }
    

    出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

    • 解决方法:
      1. 专门的算法、原则。
      2. 尽量减少同步资源的定义。
      3. 尽量避免嵌套同步。

6.5:线程的通信

  1. 使用两个线程交替打印1-100

    class Number implements Runnable {
        private int number = 1;
    
        public void run() {
            while(true) {
                synchronized (this) {
                    // 线程通信方法二
                    notify();
    
                    if(number <= 100) {
                        System.out.println(Thread.currentThread().getName() + ":" + number);
                        number++;
    
                        try{
                            // 线程通信方法一
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    public class CommunicationTest {
        public static void main(String[] args){
            Number number = new Number();
    
            Thread t1 = new Thread(number);
            Thread t2 = new Thread(number);
    
            t1.setName("线程1");
            t2.setName("线程2");
    
            t1.start();
            t2.start();
        }
    }
    
  2. 线程通信涉及到的方法

    • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
    • notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
    • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
  3. 说明

    • wait()notify()notifyAll()这三个方法必须使用在同步代码块或同步方法中。
    • wait()notify()notifyAll()这三个方法的调用这必须是同步代码块或同步方法中的同步监视器。否则会出现IllegalMonitorStateException异常。
    • wait()notify()notifyAll()这三个方法是定义在java.lang.Object类中。
  4. sleep()wait()的异同

    • 相同点

      一旦执行方法,都可以使得线程进入阻塞状态。

    • 不同点

      1. 两个方法声明的位置不同:Thread类中声明sleep()Object类中声明wait()
      2. 调用的要求不同:sleep()可以在任何场景下调用。wait()必须使用在同步代码块或同步方法中。
      3. 关于释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

6.6:JDK5.0新增线程创建方式

  1. 方式一:实现Callable接口

    • 与使用Runnable相比,Callable功能更强大一些。
      1. 相比run()方法,可以有返回值。
      2. 方法可以抛出异常。
      3. 支持泛型的返回值。
    • FutrueTask
      1. FutrueTaskFutrue接口的唯一实现类。
      2. FutrueTask同时实现了RunnableFuture接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
    //1.创建一个实现Callable的实现类
    class NumThread implements Callable{
        //2.实现call方法,将此线程需要执行的操作声明在call()中
        @Override
        public Object call() throws Exception {
            int sum = 0;
            for (int i = 1; i <= 100; i++) {
                if(i % 2 == 0){
                    System.out.println(i);
                    sum += i;
                }
            }
            return sum;
        }
    }
    
    public class ThreadNew {
        public static void main(String[] args) {
            //3.创建Callable接口实现类的对象
            NumThread numThread = new NumThread();
            //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
            FutureTask futureTask = new FutureTask(numThread);
            //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
            new Thread(futureTask).start();
    
            try {
                //6.获取Callable中call方法的返回值
                //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
                Object sum = futureTask.get();
                System.out.println("总和为:" + sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
  2. 方式二:使用线程池

    • 背景

      经常创建和销毁、使用量特别大的资源【并发情况下的线程】,对性能影响很大。

    • 思路

      提前创建好多个线程,放入线程池中,使用时直接获取,用完放回池中。可以避免频繁创建销毁、实现重复利用

    • 好处

      1. 提高响应速度【减少了创建新线程的时间】。
      2. 降低资源消耗【重复利用线程池中线程,不需要每次都创建】。
      3. 便于线程管理。
    • 线程池相关API

      1. JDK 5.0起提供了线程池相关APIExecutoServiceExecutors
      2. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
        • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
        • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
        • void shutdown():关闭连接池
      3. Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
        • Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
        • Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池
        • Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
        • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或定期执行
    class NumberThread implements Runnable{
        @Override
        public void run() {
            for(int i = 0;i <= 100;i++){
                if(i % 2 == 0){
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        }
    }
    
    public class ThreadPool {
    
        public static void main(String[] args) {
            //1. 提供指定线程数量的线程池
            ExecutorService service = Executors.newFixedThreadPool(10);
            //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
            service.execute(new NumberThread());
            //3.关闭连接池
            service.shutdown();
        }
    }
    

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

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

相关文章

vue3相比vue2性能上提升体现

vue3相比vue2&#xff0c;在 编译阶段&#xff0c;源码体积 响应式系统 都做了性能提升不以解决实际业务痛点的更新都是耍流氓1. ts的支持2. 移除了不常用的api,例如 过滤器 $on $off $once 实例方法 内联模板attribute $destroy3. 加了tree - shaking4. 移除了mixin缺点&#…

ESP32设备驱动-RFID-RC522模块驱动

RFID-RC522模块驱动 文章目录 RFID-RC522模块驱动1、RFID-RC522介绍2、硬件准备3、软件准备4、驱动实现1、RFID-RC522介绍 基于 NXP 的 MFRC522 IC 的 RC522 RFID 模块通常带有一个 RFID 卡标签和具有 1KB 内存的密钥卡标签。 最重要的是,它可以写一个标签,这样你就可以在里…

前端老赵一次给你讲透“微前端”架构

一、引言&#xff1a; 随着Web应用程序的规模和复杂度的不断增加&#xff0c;前端技术也在不断发展和演进。微前端是近年来兴起的一种前端架构模式&#xff0c;通过将大型Web应用程序拆分为小型、可独立开发和部署的模块&#xff0c;从而降低开发和维护的难度&#xff0c;同时…

【LeetCode与《代码随想录》】二叉树篇:做题笔记与总结-JavaScript版

文章目录代码随想录144. 二叉树的前序遍历94. 二叉树的中序遍历145. 二叉树的后序遍历102.二叉树的层序遍历226.翻转二叉树101. 对称二叉树104.二叉树的最大深度111.二叉树的最小深度222.完全二叉树的节点个数110.平衡二叉树257. 二叉树的所有路径404.左叶子之和513.找树左下角…

盘点曾经很火但消失了的8个软件

目录 1、飞信 3、暴风影音 4、千千静听 5、虾米音乐 6、快车下载 7、人人网 8、QQ农场 今天小编给大家分享曾经很火但消失了的8个软件&#xff0c;你都用过吗&#xff1f; 1、飞信 飞信是中国移动通信集团公司推出的一款短信、语音、视频通信应用程序。它于2007年推出&a…

JDK的动态代理(powernode 文档)(内含源代码)

JDK的动态代理&#xff08;powernode 文档&#xff09;&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87546086 一、动态代理 目录JDK的动态代理&#xff08;powernode 文档&#xff09;&#xff0…

什么是L1和L2正则化,以及它们有什么区别

一、L1和L2正则化是什么&#xff1f; 在防止过拟合的方法中有L1正则化和L2正则化&#xff0c;L1和L2是正则化项&#xff0c;又叫做惩罚项&#xff0c;是为了限制模型的参数&#xff0c;防止模型过拟合而加在损失函数后面的一项。 在二维的情况下&#xff0c;黄色的部分是L2和…

【云原生】rancher2.6部署MySQL—2023.03

文章目录概要1. 准备NFS服务器1.1 安装nfs1.2 创建挂载路径1.3 启动NFS服务2. 所有node节点上安装NFS服务3. rancher上部署MySQL3.1 创建PV3.2 创建PVC3.3 创建服务发现3.4 部署MySQL服务4. 测试概要 本文以单master节点为例&#xff0c;部署mysql&#xff0c;多master&#x…

Ubutun设置SSH远程登录

Ubutun设置SSH远程登录一、安装ssh-server二、配置ssh三、防火墙配置一、安装ssh-server 在需要远程登录的设备中安装ssh-server sudo apt update sudo apt install openssh-server出现提示时&#xff0c;输入密码&#xff0c;然后按Enter继续安装。安装完毕后&#xff0c;使…

2023金三银四应届生求职面试指南

一、应届生优势 划重点&#xff0c;一定要走校招;千万不要等毕业之后再想着找工作&#xff0c;在毕业前就要敲定落实;否则&#xff0c;就真的该焦虑了。要知道应届生的身份是一个很吃香的身份;只有应届生可以走校园招聘。 1、那校园招聘跟社会招聘有多大的差距?? 这么说吧&…

微信聊天的一个创新方向

开门见山&#xff0c;简单&#xff08;简陋&#xff09;展示下新的聊天界面&#xff1a; 注意到除了原本的发送键&#xff0c;多了几个别的按钮。为了对比方便&#xff0c;先放上当前的聊天方式&#xff1a; 给兄弟发消息时&#xff0c;点击发送键&#xff0c;显示的是如下发…

【华为机试真题详解 Python实现】静态扫描最优成本【2023 Q1 | 100分】

文章目录前言题目描述输入描述输出描述示例 1输入&#xff1a;输出&#xff1a;示例 2输入&#xff1a;输出&#xff1a;题目解析参考代码前言 《华为机试真题详解》专栏含牛客网华为专栏、华为面经试题、华为OD机试真题。 如果您在准备华为的面试&#xff0c;期间有想了解的…

【微信小程序项目实战】TodoList-环境配置(1)

目录前言简介环境配置TDesign图片页面文件文件基础配置app.wxssapp.jsontodo.json前言 本项目依据开源项目:点击前往 GITHUB 仓库 仿照搭设而成&#xff0c;并主要对其中原理以及方法做出详细分析解读&#xff0c;望大家多多支持原作者&#xff01; 简介 本项目将使用最新版…

C#开发的OpenRA的游戏主界面怎么样创建5

继续游戏主界面创建的主题, 前面已经介绍到怎么样创建一个OpenRA的帐号显示, 接着下来介绍中间显示新闻的消息窗口,如下图所示: 这个界面看起来比较简单,只有一个下拉按钮显示,但是背后的实现是比较复杂的。 因为它要实现一个对话框的窗口显示,那需要编写的代码和设计思…

字节3次都没裁掉的7年老测试。掌握设计业务与技术方案,打开上升通道!

前言职场中的那些魔幻操作&#xff0c;研发最烦的是哪个&#xff1f;“面对业务需求的时候&#xff0c;可能都听过这样一句话&#xff1a;这个很简单&#xff0c;直接开发&#xff0c;三天内上线&#xff1b;”朋友说&#xff1a;“产品听了流泪&#xff0c;测试见了崩溃&#…

Linux系统CPU占用率较高问题排查思路

作为工程师&#xff0c;在日常工作中我们会遇到 Linux服务器上出现CPU负载达到100%居高不下的情况&#xff0c;如果CPU 持续跑高&#xff0c;则会影响业务系统的正常运行&#xff0c;带来企业损失。对于CPU过载问题通常使用以下两种方式即可快速定位&#xff1a;方法一第一步&a…

STC单片机RTC时钟使用介绍

STC单片机RTC时钟使用介绍 ✨目前支持RTC功能的STC单片机型号只有带型号后面带TL\T的STC8以及STC8H8K64U B/C/D版本以及STC32G型号的单片机支持此功能.手上的STC8H8K64U单片机,B版本在实际测试中并没有成功,使用STC32G测试没有问题。 🎞使用STC8H8K64U B版本打印效果: 🔖…

springboot整合Quartz(1)

文章目录前言一 理论基础1.1 小顶堆(平衡二叉堆)1.2 小顶堆的存取方式1.2.1 插入顶堆元素1.2.2 删除顶堆元素1.3 任务与小顶堆1.3 时间轮算法二 Spring Boot集成JDK定时任务2.1 TaskQueue源码分析2.2 TimerThread源码分析2.2.1 Timer构造器2.2.2 Timer类中的执行方法2.2.3 Time…

深浅拷贝——利用模拟实现basic_string深入理解

深浅拷贝——利用模拟实现basic_string深入理解 一、深浅拷贝的基本概念 深拷贝和浅拷贝都是指在对象复制时&#xff0c;复制对象的内存空间的方式。 1.1 深浅拷贝的不同之处 浅拷贝是指将一个对象的所有成员变量都直接拷贝给另一个对象&#xff0c;包括指针成员变量&#…

Matlab进阶绘图第5期—风玫瑰图(WindRose)

风玫瑰图(Wind rose diagram)是一种特殊的极坐标堆叠图/统计直方图&#xff0c;其能够直观地表示某个地区一段时期内风向、风速的发生频率。 风玫瑰图在建筑规划、环保、风力发电、消防、石油站设计、海洋气候分析等领域都有重要作用&#xff0c;所以在一些顶级期刊中也能够看…