当前位置: 首页 > news >正文

⛳ Java多线程 一,线程基础

线程基础

  • ⛳ Java多线程 一,线程基础
  • 🐾 一,线程基础
      • 💭 1.1,什么是程序,进程,线程
      • 🏭 1.2,什么是并行和并发
      • 👣 1.3,线程使用的场景
      • 🎨 1.4,创建线程
        • 1.4.1,继承 `Thread`类的方式
        • 1.4.2,实现`Runanble`接口:
        • 1.4.3,`Thread` 常见方法
        • 1.4.4,`sleep()`方法
        • 1.4.5,线程优先级
        • 1.4.6,守护线程
        • 1.4.7,线程合并
        • 1.4.8,线程退出
      • 📢 1.5,线程的生命周期
        • 1.5.1,线程的调度与时间片
        • 1.5.2,线程状态
        • 1.5.3,多线程自增 i++ 和线程执行原理

⛳ Java多线程 一,线程基础

当涉及多线程编程时,我们可以同时执行多个任务,从而提高程序的性能和响应性。然而,多线程编程也带来了一些挑战,比如线程同步和资源共享。在本教程中,我们将介绍Java中的多线程编程,包括创建和管理线程,线程同步,以及常见的多线程问题和解决方案。

🐾 一,线程基础

💭 1.1,什么是程序,进程,线程

在计算机科学中,程序(Program),进程(Process)和线程(Thread)是三个重要的概念,它们都与执行计算机任务和程序相关。

  1. 程序(Program):

    程序是一系列指令的集合,这些指令按照特定的顺序组织,用于完成特定的任务或执行特定的操作。程序是静态的,它们只是存储在计算机磁盘或存储设备中,并不直接执行。当我们想要运行一个程序时,操作系统会将程序加载到内存中,并将其转换为进程,然后才能执行其中的指令。

  2. 进程(Process):

    进程是计算机程序在执行时的实例。它是计算机中正在运行的程序的活动副本。每个进程都有自己独立的内存空间,包含程序代码、数据、堆栈等信息。进程之间相互隔离,一个进程的崩溃不会影响其他进程的稳定性。每个进程在操作系统中都有自己的标识符(Process ID),它可以用来管理和监控进程的状态。

    进程有以下特点:

    • 进程之间相互隔离,一个进程的崩溃不会影响其他进程的稳定性。每个进程在操作系统中都有自己的标识符(Process ID),它可以用来管理和监控进程的状态。
    • 资源分配:每个进程拥有自己的内存空间和系统资源。
    • 切换开销:在进程之间切换会导致一定的开销,因为需要保存和恢复各个进程的上下文。
    1. 线程(Thread):

      线程是进程中的执行单元,每个进程可以包含多个线程。线程与进程共享相同的内存空间,因此它们可以更轻松地相互通信。多线程在多核处理器上能够实现并行执行,从而提高程序的性能和响应性。

      线程有以下特点:

      • 共享资源:线程之间共享进程的内存和资源,可以更方便地交换数据和信息。

      • 轻量级:相较于进程,线程的创建、切换和销毁开销较小。

      • 同步问题:多线程共享资源可能导致同步问题,需要采取同步机制来避免数据冲突。

        总结:

        程序是一组指令的集合,进程是程序在执行时的实例,而线程是进程中的执行单元。进程之间相互独立,而线程共享同一进程的资源。多线程在并行处理和提高程序性能方面具有优势,但同时也需要注意处理线程同步问题。

🏭 1.2,什么是并行和并发

并行(Parallel /ˈpærəlel/ )和并发(Concurrent /kənˈkʌrənt/)是两个与计算机执行任务相关的重要概念。

  1. 并行(Parallel):

    并行是指同时执行多个任务或操作。在计算机中,当多个处理器核心或计算单元同时执行不同的任务或同一个任务的不同部分时,我们称之为并行执行。这样可以显著提高计算机系统的处理能力和效率,特别时在多核处理器上。

    举例来说,如果有一个任务要处理一系列的数据,那么在并行处理中,不同的处理器核心可以同时处理这些数据的不同部分,从而更快地完成任务。

  2. 并发(Concurrent):

    并发是指同时处理多个任务,但并不一定是同时执行这些任务。在计算机中,并发执行是通过快速的任务切换和调度来实现的,操作系统会以非常小的时间片轮流执行多个任务,让它们看起来是同时进行的。

    举例来说,如果有两个任务要处理,操作系统可以分别给它们分配时间片,让它们交替执行,从用户的角度来看,好像这两个任务在同时进行。

    关键区别: 并行是真正的同时执行多个任务,需要多个处理器核心或计算单元支持。而并发是通过快速的切换和调度,让多个任务交替执行,从用户的角度来看,好像这些任务在同时进行,但实际上是在时间上错开的。

    总结:

    并行是真正的同时执行多个任务,利用多核处理器等技术提高计算效率;而并发是通过任务切换和调度,让多个任务交替执行,实现任务间的快速切换,从用户角度看起来是同时执行的。并行适合利用多核处理器等资源来加速计算,而并发适合在单核处理器上实现任务的高效利用。

👣 1.3,线程使用的场景

线程在计算机编程和软件开发中有许多使用场景。

它们主要用于以下情况:

  1. 并发执行:线程使得程序能够同时处理多个任务,特别是在多核处理器上能够实现真正的并行执行。例如,一个计算密集型的应用程序可以将任务分成多个线程,在多核处理器上同时执行,提高整体性能。
  2. 多任务处理:线程可以用于在单个应用程序内执行多个任务,使得程序能够同时响应多个用户请求或事件。例如,一个服务器应用程序可以使用多线程来同时处理多个客户端请求。
  3. 用户界面响应:在图形用户界面(GUI)应用程序中,线程用于保持用户界面的响应性。将耗时的任务(如文件加载、网络请求等)放在后台线程中执行,以免阻塞用户界面的操作。
  4. 并行算法:在某些计算密集型任务中,可以使用线程实现并行算法,将计算任务分成多个部分并在不同线程中并行执行,从而加快计算速度。
  5. 服务器应用:在服务器端应用程序中,线程用于同时处理多个客户端连接请求,实现高并发处理能力。
  6. 多媒体处理:在音频、视频处理等应用中,线程可用于同时处理多个媒体流,实现流畅的播放和录制。
  7. 游戏开发:在游戏开发中,线程通常用于同时处理游戏逻辑、图形渲染和用户输入,以提供流畅的游戏体验。

需要注意的是,虽然线程在很多情况下能够提高程序的性能和响应性,但多线程编程也带来了一些挑战,如数据同步与竞争条件。正确地处理线程同步和资源共享问题对于编写稳定和可靠的多线程应用程序至关重要。因此,在使用线程时,开发人员需要仔细考虑并采取适当的同步措施,以避免潜在的问题。

🎨 1.4,创建线程

有两种方式可以创建线程:

1,继承Thread 类并重写 run()方法;

2,实现Runnable接口;

1.4.1,继承 Thread类的方式

  1. 定义子类继承Thread类。
  2. 类中重写Thread类中的run方法。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。

案例:

启动一个线程,在线程中执行1-10000的偶数打印工作:

public class MyThread extends Thread {@Overridepublic void run() {for (int i = 1; i <= 10000; i++) {if (i % 2 == 0) {//Thread.currentThread().getName():得到线程的名字System.out.println(Thread.currentThread().getName() + "\t" + i);}}}
}

测试:

public class Test1 {public static void main(String[] args) {MyThread myThread1 = new MyThread();myThread1.start();MyThread myThread2 = new MyThread();myThread2.start();System.out.println(Thread.currentThread().getName() + "  main 线程 over");}
}
        JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞System.out.println("main over");
  • 如果子线程执行,进程不会停止。

1.4.2,实现Runanble接口:

  • 定义子类,实现Runnable接口。
  • 类中重写Runnable接口中的run方法。
  • 通过Thread类含参构造器创建线程对象。
  • 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  • 调用Thread类的start方法:开启线程, 调用Runnable子类接口的run方法。
public class ThreadDemo {public static void main(String[] args) {//方式1:Thread子类,启动MyThread thread1 = new MyThread();thread1.start();//方式2:Runable方式(推荐的方式)//优势:可以实现多继承,比如继承BaseDao,然后再实现RunnableMyTask task = new MyTask();Thread thread2 = new Thread(task);thread2.start();//方式3:匿名内部类new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName() +  "\t" +  i);}}}).start();}}//方式1
class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName() +  "\t" +  i);}}
}//方式2
//优势:可以实现多继承,比如继承BaseDao,然后再实现Runnable
class MyTask implements Runnable {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName() +  "\t" +  i);}}
}

1.4.3,Thread 常见方法

  • 构造函数

    • Thread(): 创建新的Thread对象
    • Thread(String threadname): 创建线程并指定线程实例名
    • Thread(Runnable target): 指定创建线程的目标对象,它实现了Runnable接口中的run方法
    • Thread(Runnable target, String name): 创建新的Thread对象
  • void start(): 启动线程,并执行对象的run()方法

  • run(): 线程在被调度时执行的操作

  • String getName(): 返回线程的名称

  • **void setName(String name)😗*设置该线程名称

  • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类

  • static void yield(): 线程让步

    • 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
    • 若队列中没有同优先级的线程,忽略此方法
  • join() : 当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止

  • static void sleep(long millis): (指定时间:毫秒) 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。

  • stop(): 强制线程生命期结束,不推荐使用

  • boolean isAlive(): 返回boolean,判断线程是否还活着

1.4.4,sleep()方法

指定线程休眠的时间,单位毫秒,让出cpu时间片,其他线程可以抢占时间片

public class MyTask implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {//Thread.currentThread().getName():得到线程的名字System.out.println(Thread.currentThread().getName() + "\t" + i);try {Thread.sleep(1000);Thread.yield();} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

1.4.5,线程优先级

  • 线程的优先级等级

    • MAX_PRIORITY: 10
    • MIN _PRIORITY: 1
    • NORM_PRIORITY: 5
    public static void main(String[] args) {MyTask myTask = new MyTask();Thread thread1 = new Thread(myTask, "t1");thread1.setPriority(Thread.MIN_PRIORITY);thread1.start();Thread thread2 = new Thread(myTask, "t2");thread1.setPriority(Thread.MAX_PRIORITY);thread2.start();}

1.4.6,守护线程

  • 其他线程都执行结束,守护线程自动结束
  • 守护启动子线程,也是守护线程
  • 守护线程的语法thread.(*setDaemon(true)*设置守护线程
public class Test2 {public static void main(String[] args) {MyThread myThread1 = new MyThread();
//        myThread1.setDaemon(true);myThread1.start(); //守护线程, gc线程,jvm线程结束gc会自动结束JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞System.out.println("main over");}}class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("a");}}
}

1.4.7,线程合并

image-20230731101929769

案例:

public class Test1 {/*** CountDownLatch:可以实现相同的效果* @param args*/public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "\t" + i);try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t1.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() +  "  main orver");}}

image-20230731102014846

1.4.8,线程退出

  • stop():不推荐,线程退出方式粗暴,不管线程正在执行的任务,直接退出,可能丢失数据。

    image-20230731102216532

    public class Test1 {public static void main(String[] args) {Thread t1 = new Thread(new MyTask());t1.start();Scanner in = new Scanner(System.in);System.out.println("输入1/0:0表示退出");int i = in.nextInt(); ///主线程进入IO阻塞if (i == 0) {t1.stop();}System.out.println("main over");}static class MyTask implements Runnable {@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
    }
    
  • 中断信号interrupt

    image-20230731102257699

    • interrupt():发送中断信号(true)

      • 如果线程在阻塞状态,比如sleep(),join(),wait(),这时接收到中断信号会抛出一个异常InterruptException,同时中断信号清除(false)
      • 只是发送信号,不会对线程产生影响
    • **static interrupted():**得到中断信号(true),然后把中断信号设置成false

    • **isInterrupted():**得到中断信号,不会清除中断信号

    public class Test1 {public static void main(String[] args) {Thread t1 = new Thread(new MyTask());t1.start();JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞t1.interrupt(); //发送中断信号给t1System.out.println("main over");}static class MyTask implements Runnable {@Overridepublic void run() {while (true) {System.out.println("a");try {Thread.sleep(500000);} catch (InterruptedException e) {System.out.println("bbbbbbbbbbbbbbb");e.printStackTrace();Thread.currentThread().interrupt(); //再次发送中断信号,中断信号发给阻塞线程,抛出Interrupt异常,中断信号清除
    //                    throw new RuntimeException(e);}//得到中断信号,优雅的退出if (Thread.interrupted()) {break;}}}}
    }
    

📢 1.5,线程的生命周期

1.5.1,线程的调度与时间片

​ 由于 CPU 的计算频率非常高,每秒计算数十亿次,于是,可以将 CPU 的时间从毫秒的维度进行分段,每一小段叫做一个 CPU 时间片。不同的操作系统、不同的处理器,线程的 CPU 时间片长度都不同。假定操作系统的线程一个时间片的时间长度为 20 毫秒(比如 Windows XP),在一个 2GHz 的 CPU 上,那么一个时间片可以进行计算的次数是: 20 亿/(1000/20) =4 千万次,也就是说,一个时间片内的计算量是非常巨大的。 目前操作系统中主流的线程调度方式大都是:基于 CPU 时间片方式进行线程调度。线程只有得到 CPU 时间片,才能执行指令,处于执行状态;没有得到时间片的线程,处于就绪状态,等待系统分配下一个 CPU 时间片。由于时间片非常短,在各个线程之间快速地切换,表现出来特征是很多个线程在“同时执行”或者“并发执行”。线程的调度模型,目前主要分为两种调度模型:分时调度模型、抢占式调度模型。

(1)分时调度模型——系统平均分配 CPU 的时间片,所有线程轮流占用 CPU。分时调度模型在时间片调度的分配上,所有线程人人平等。

下图就是一个分时调度的简单例子:三个线程,轮流得到 CPU 时间片;一个线程执行时,另外两个线程处于就绪状态

image-20230731102854819

(2)抢占式调度模型——系统按照线程优先级分配 CPU 时间片。优先级高的线程,优先分配 CPU 时间片;如果所有的就绪线程的优先级相同,那么会随机选择一个;优先级高的线程获取的 CPU 时间片相对多一些。 由于目前大部分操作系统都是使用抢占式调度模型进行线程调度。 Java 的线程管理和调度是委托给了操作系统完成的,与之相对应, Java 的线程调度也是使用抢占式调度模型

1.5.2,线程状态

操作系统,线程的状态

image-20230731103051812

java的线程状态

得到java的线程状态

public Thread.State getState(); //返回当前线程的执行状态,一个枚举类型值

Thread.State是一个内部枚举类,定义了6个枚举常量,分别代表Java线程的6种抓过你太,具体如下:

public static enum State {NEW, //新建RUNNABLE, //可执行:包含操作系统的就绪、运行两种状态BLOCKED, //阻塞  -> 操作系统线程中的阻塞WAITING, //等待  -> 操作系统线程中的阻塞TIMED_WAITING, //计时等待 -> 操作系统线程中的阻塞TERMINATED; //终止}

Thread.State定义的6中状态中,有四种是比较常见的状态,他们是:NEW状态,RUNNABLE状态,TERMINATED状态,TIMED_WAITING状态。

接下来,将线程的6种状态以及各种状态的进入条件,做个总结:

  1. NEW状态:通过 new Thread(...)已经创建线程,但尚未调用start()启动线程,该线程处于NEW(新建)状态。虽然前面介绍了4种创建线程,但是其中的其他三种方式,本质上都是通过new Thread()创建的线程,仅仅是创建了不同的target执行目标示例(如Runnable实例)。

  2. RUNNABLE状态:Java 把就绪(Ready)和 执行(Running)两种状态合并为一种状态:可执行(RUNNABLE)状态(或者可运行状态)。调用了线程的start()实例方法后,线程就处于就绪状态了;此线程获取到CPU时间片后,开始执行run()方法种的业务代码,线程处于执行状态。

    image-20230731142105785

    • 就绪状态:就绪状态仅仅表示线程具备运行资格,如果没有被操作系统的调度程序挑选中,线程就永远是就绪状态;当前线程进入就绪状态的条件,大致包括以下几种:

      • 调用线程的start()方法,此线程进入就绪状态。
      • 当前线程的执行时间片用完。
      • 线程睡眠(sleep)操作结束。
      • 对其他线程合入(join)操作结束。
      • 等待用户输入结束。
      • 线程争抢到对象锁(Object Monitor)。
      • 当前线程调用了yield()方法,让出CPU执行权限。
    • 执行状态:线程调度程序从就绪状态的线程中选择一个线程,作为当前线程时线程所处的状态。这也是线程进入执行状态的唯一方式。

    • BLOCKED 状态: 处于阻塞(BLOCKED)状态的线程并不会占用CPU资源,以下情况会让线程进入阻塞状态:

      1. 线程等待获取锁 :等待获取一个锁,而该锁被其他线程持有,则该线程进入阻塞状态。当其他线程释放了该锁,并且线程调度器允许该线程持有该锁时,该线程退出阻塞状态。
      2. IO 阻塞:线程发起了一个阻塞式 IO 操作后,如果不具备 IO 操作的条件,线程会进入阻塞状态。 IO 包括磁盘 IO、 网络 IO 等。 IO 阻塞的一个简单例子:线程等待用户输入内容后继续执行。
    • WAITING 状态:处于 WAITING(无限期等待)状态的线程不会被分配 CPU 时间片,需要被其他线程显式地唤醒,才会进入就绪状态。线程调用以下 3 种方法,会让自己进入无限等待状态:

      1. Object.wait() 方法,对应的唤醒方式为: Object.notify() / Object.notifyAll()。
      2. Thread.join() 方法,对应的唤醒方式为:被合入的线程执行完毕。
      3. LockSupport.park() 方法,对应的唤醒方式为: LockSupport.unpark(Thread)。
    • TIMED_WAITING 状态:处于 TIMED_WAITING(限时等待)状态的线程不会被分配 CPU 时间片,如果指定时间之内没有被唤醒,限时等待的线程会被系统自动唤醒,进入就绪状态。以下 3 个方法会让线程进入限时等待状态:

      • Thread.sleep(time) 方法,对应的唤醒方式为: sleep 睡眠时间结束。
      • Object.wait(time) 方 法 , 对 应 的 唤 醒 方 式 为 : 调 用 Object.notify() /Object.notifyAll()去主动唤醒,或者限时结束。
      • LockSupport.parkNanos(time)/parkUntil(time) 方法,对应的唤醒方式为:线程调用配套的 LockSupport.unpark(Thread)方法结束,或者线程停止(park)时限结束。

      进入 BLOCKED 状态、 WAITING 状态、 TIMED_WAITING 状态的线程都会让出 CPU 的使用权;另外,等待或者阻塞状态的线程被唤醒后,进入 Ready 状态,需要重新获取时间片才能接着运行。

    • TERMINATED 状态:线程结束任务之后,将会正常进入 TERMINATED(死亡)状态;或者说在线程执行过程中发生了异常(而没有被处理),也会导致线程进入死亡状态。

1.5.3,多线程自增 i++ 和线程执行原理

4个线程自增一个堆(共享的)里的对象的值

public class Test {private int i =0;public void f1() {i++;}
}

i++的反编译结果

javap -c Test.class

image-20230801211556153

image-20230801211859089

线程产生时 ,为每个线程的一个虚拟机栈分配1M内存

-Xss128K:指定没有线程的栈大小是128K

自增类:

public class Plus {private int amount = 0;public void selfPlus() {amount ++;}public int getAmount() {return amount;}}

自增任务类

public class PlusTask implements Runnable {private Plus plus;public PlusTask() {}public PlusTask(Plus plus) {this.plus = plus;}@Overridepublic void run() {for (int i = 0; i < 100000000; i++) {plus.selfPlus();}}}

测试

    public static void main(String[] args) throws InterruptedException {Plus plus = new Plus();PlusTask plusTask = new PlusTask(plus);Thread t1 = new Thread(plusTask);Thread t2 = new Thread(plusTask);Thread t3 = new Thread(plusTask);Thread t4 = new Thread(plusTask);t1.start();t2.start();t3.start();t4.start();t1.join();t2.join();t3.join();t4.join();System.out.println("实际的值 = " + plus.getAmount());}

相关文章:

⛳ Java多线程 一,线程基础

线程基础 ⛳ Java多线程 一&#xff0c;线程基础&#x1f43e; 一&#xff0c;线程基础&#x1f4ad; 1.1&#xff0c;什么是程序&#xff0c;进程&#xff0c;线程&#x1f3ed; 1.2&#xff0c;什么是并行和并发&#x1f463; 1.3&#xff0c;线程使用的场景&#x1f3a8; 1.…...

【iOS】多线程 锁问题总结

文章目录 前言1. 你理解的多线程优点缺点 2. atomic 和 nonatomic 的区别及其作用3. GCD的队列类型 - 三种队列类型4. GCD的死锁问题5. 多线程之间的区别和联系6. 进程和线程&#xff1f;进程间的通信方式线程间的通信方式 6. iOS的线程安全手段如何保证 前言 iOS 锁和多线程的…...

Pytorch深度学习-----神经网络之池化层用法详解及其最大池化的使用

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…...

Docker啥是数据持久化?

文章目录 数据持久化数据卷相关命令创建读写数据卷创建只读数据卷数据卷共享数据卷容器实现数据卷共享nginx实现数据卷共享nfs总结 Dockerfile持久化Dockerfile方式docker run总结 数据持久化 ​ 在容器层的 UnionFS&#xff08;联合文件系统&#xff09;中对文件/目录的任何修…...

CGAL 线段简化算法(2D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 线段简化是指:在减少一组折线中顶点数量的同时,尽可能保持整体形状的过程。CGAL中提供了一种迭代算法:通过从一条折线上移除顶点 q q q,迭代地将边 ( p , q...

在CentOS 7上挂载硬盘到系统的步骤及操作

目录 1&#xff1a;查询未挂载硬盘2&#xff1a;创建挂载目录3&#xff1a;检查磁盘是否被分区4&#xff1a;格式化硬盘5&#xff1a;挂载目录6&#xff1a;检查挂载状态7&#xff1a;设置开机自动挂载总结&#xff1a; 本文介绍了在CentOS 7上挂载硬盘到系统的详细步骤。通过确…...

螺旋矩阵(JS)

螺旋矩阵 题目 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]示例 2&#xff1a; 输入&#xff…...

C#常用数学插值法

目录 1、分段线性插值 2、三次样条插值 3、拉格朗日插值 &#xff08;1&#xff09;一元全区间不等距插值 &#xff08;2&#xff09;一元全区间等距插值 4、埃尔米特插值 &#xff08;1&#xff09;埃尔米特不等距插值 &#xff08;2&#xff09;埃尔米特等距插值 1、…...

ELK日志管理平台架构和使用说明

一、部署架构 二、服务注册 2.1 日志解析服务 服务名&#xff1a;日志解析服务&#xff08;Logstash&#xff09; 服务默认端口&#xff1a;9600 2.2 日志查询服务 服务名&#xff1a;日志查询服务&#xff08;Kibana&#xff09; 服务默认端口&#xff1a;5601 三、对接…...

抖音短视频seo矩阵系统源码开发部署技术分享

抖音短视频的SEO矩阵系统是一个非常重要的部分&#xff0c;它可以帮助视频更好地被搜索引擎识别和推荐。以下是一些关于开发和部署抖音短视频SEO矩阵系统的技术分享&#xff1a; 一、 抖音短视频SEO矩阵系统的技术分享&#xff1a; 关键词研究&#xff1a;在开发抖音短视频SEO矩…...

docker 部署一个单节点的rocketmq

拉取镜像 sudo docker pull rocketmqinc/rocketmq创建数据挂载目录 mkdir -p /docker/rocketmq/data/namesrv/logs mkdir -p /docker/rocketmq/data/namesrv/store mkdir -p /docker/rocketmq/data/broker/logs mkdir -p /docker/rocketmq/data/broker/store /docker/…...

MySQL优化

目录 一. 优化 SQL 查询语句 1.1. 分析慢查询日志 1.2. 优化 SQL 查询语句的性能 1.2.1 优化查询中的索引 1.2.2 减少表的连接&#xff08;join&#xff09; 1.2.3 优化查询语句中的过滤条件 1.2.4 避免使用SELECT * 1.2.5 优化存储过程和函数 1.2.6 使用缓存 二. 优化表结构…...

【C++】总结9

文章目录 C从源代码到可执行程序经过什么步骤静态链接和动态链接类的对象存储空间C的内存分区内存池在成员函数中调用delete this会出现什么问题&#xff1f;如果在类的析构函数中调用delete this&#xff0c;会发生什么&#xff1f; C从源代码到可执行程序经过什么步骤 预处理…...

C++报错 XX does not name a type;field `XX’ has incomplete type解决方案

C报错 XX does not name a type&#xff1b;field XX’ has incomplete type解决方案 两个C编译错误及解决办法–does not name a type和field XX’ has incomplete type 编译错误一&#xff1a;XX does not name a type 编译错误二&#xff1a;field XX’ has incomplete t…...

28.利用fminsearch、fminunc 求解最大利润问题(matlab程序)

1.简述 1.无约束&#xff08;无条件&#xff09;的最优化 fminunc函数 : - 可用于任意函数求最小值 - 统一求最小值问题 - 如求最大值问题&#xff1a; >对函数取相反数而变成求最小值问题&#xff0c;最后把函数值取反即为函数的最大值。 使用格式如下 1.必须预先把函数存…...

图像 检测 - FCOS: Fully Convolutional One-Stage Object Detection (ICCV 2019)

FCOS: Fully Convolutional One-Stage Object Detection - 全卷积一阶段目标检测&#xff08;ICCV 2019&#xff09; 摘要1. 引言2. 相关工作3. 我们的方法3.1 全卷积一阶目标检测器3.2 FCOS的FPN多级预测3.3 FCOS中心度 4. 实验4.1 消融研究4.1.1 FPN多级预测4.1.2 有无中心度…...

C# NDArray System.IO.FileLoadException报错原因分析

C# NDArray System.IO.FileLoadException 报错原因分析&#xff1a; 1.NuGet程序包版本有冲突 2.统一项目版本 1.打开解决方案NuGet程序包设置 2.查看是否有版本冲突 3.统一版本冲突...

快速响应,上门维修小程序让您享受无忧生活

随着科技的不断发展和智能手机的普及&#xff0c;上门维修小程序成为了现代人生活中越来越重要的一部分。上门维修小程序通过将维修服务与互联网相结合&#xff0c;为用户提供了更加便捷、高效的维修服务体验。下面将介绍上门维修小程序开发的优势。   提供便捷的预约方式&am…...

05、性能分析思路?

工具操作&#xff1a;包括压力工具、监控工具、剖析工具、调试工具。数值理解&#xff1a;包括上面工具中所有输出的数据。趋势分析、相关性分析、证据链分析&#xff1a;就是理解了工具产生的数值之后&#xff0c;还要把它们的逻辑关系想明白。这才是性能测试分析中最重要的一…...

【编程语言 · C语言 · calloc和realloc】

【编程语言 C语言 calloc和realloc】https://mp.weixin.qq.com/s?__bizMzg4NTE5MDAzOA&mid2247491544&idx1&sn72d8f9931cfa7ce7441a3248475ab619&chksmcfade321f8da6a374a5935bb46441a03a007c0589db6b8afa8c1991854d632a3201553e37b0b&payreadticketHGy…...

机器学习分布式框架ray运行pytorch实例

Ray是一个用于分布式计算的开源框架&#xff0c;它可以有效地实现并行化和分布式训练。下面是使用Ray来实现PyTorch的训练的概括性描述&#xff1a; 安装Ray&#xff1a;首先&#xff0c;需要在计算机上安装Ray。你可以通过pip或conda来安装Ray库。 准备数据&#xff1a;在使用…...

TypeScript 【type】关键字的进阶使用方式

导语&#xff1a; 在前面章节中&#xff0c;我们了解到 TS 中 type 这个关键字&#xff0c;常常被用作于&#xff0c;定义 类型别名&#xff0c;用来简化或复用复杂联合类型的时候使用。同时也了解到 为对象定义约束接口类型 的时候所使用的是 Interfaces。 其实对于前面&#…...

策略路由实现多ISP接入Internet

组网需求&#xff1a; 企业分别从ISP1和ISP2租用了一条链路 PC3用户上网访问Server1时走ISP1PC4用户上网访问Server1时走ISP2 拓扑图 一、ISP1 运营商 R1路由器 <Huawei>sys [Huawei]sys R1 [R1]un in en[R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip addr 2.2.2.2 2…...

Socket本质、实战演示两个进程建立TCP连接通信的过程

文章目录 Socket是什么引入面试题, 使你更深刻的理解四元组 Socket网络通信大体流程实战演示TCP连接建立过程需要用到的linux 查看网络的一些命令测试的程序一些准备工作启动服务端, 并没有调用accept启动客户端开启服务accept Socket是什么 通俗来说,Socket是套接字,是一种编…...

java学习路程之篇四、进阶知识、石头迷阵游戏、绘制界面、打乱石头方块、移动业务、游戏判定胜利、统计步数、重新游戏

文章目录 1、绘制界面2、打乱石头方块3、移动业务4、游戏判定胜利5、统计步数6、重新游戏7、完整代码 1、绘制界面 2、打乱石头方块 3、移动业务 4、游戏判定胜利 5、统计步数 6、重新游戏 7、完整代码 java之石头迷阵单击游戏、继承、接口、窗体、事件、组件、按钮、图片...

Git全栈体系(三)

第六章 GitHub 操作 一、创建远程仓库 二、远程仓库操作 命令名称作用git remote -v查看当前所有远程地址别名git remote add 别名 远程地址起别名git push 别名 分支推送本地分支上的内容到远程仓库git clone 远程地址将远程仓库的内容克隆到本地git pull 远程库地址别名 远…...

JMeter发送get请求并分析返回结果

在实际工作的过程中&#xff0c;我们通常需要模拟接口&#xff0c;来进行接口测试&#xff0c;我们可以通过JMeter、postman等多种工具来进行接口测试&#xff0c;但是工具的如何使用对于我们来说并不是最重要的部分&#xff0c;最重要的是设计接口测试用例的思路与分析结果的能…...

HTML笔记(1)

介绍 浏览器中内置了HTML的解析引擎&#xff0c;通过解析标记语言来展现网页&#xff1b;HTML标签都是预定义好的&#xff1b;Java工程师&#xff1a;后台代码的编写&#xff0c;和数据库打交道&#xff0c;把数据给网页前端的工程师&#xff1b;网页前端工程师&#xff1a;写H…...

重新审视MHA与Transformer

本文将基于PyTorch源码重新审视MultiheadAttention与Transformer。事实上&#xff0c;早在一年前博主就已经分别介绍了两者&#xff1a;各种注意力机制的PyTorch实现、从零开始手写一个Transformer&#xff0c;但当时的实现大部分是基于d2l教程的&#xff0c;这次将基于PyTorch…...

Docker 全栈体系(七)

Docker 体系&#xff08;高级篇&#xff09; 五、Docker-compose容器编排 1. 是什么 Compose 是 Docker 公司推出的一个工具软件&#xff0c;可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml&#xff0c;写好多个容器之间的调…...