【JavaEE初阶】线程 和 thread

news2024/12/23 9:46:57
本节⽬标
  • 认识多线程
  • 掌握多线程程序的编写
  • 掌握多线程的状态

一. 认识线程(Thread)

1概念

1) 线程是什么

       

         ⼀个线程就是⼀个 "执⾏流". 每个线程之间都可以按照顺序执⾏⾃⼰的代码. 多个线程之间 "同时" 执⾏着多份代码.

        还是回到我们之前的银⾏的例⼦中。之前我们主要描述的是个⼈业务,即⼀个⼈完全处理⾃⼰的业务。我们进⼀步设想如下场景:

        ⼀家公司要去银⾏办理业务,既要进⾏财务转账,⼜要进⾏福利发放,还得进⾏缴社保。
        如果只有张三⼀个会计就会忙不过来,耗费的时间特别⻓。为了让业务更快的办理好,张三⼜找来两位同事李四、王五⼀起来帮助他,三个⼈分别负责⼀个事情,分别申请⼀个号码进⾏排队,⾃此就有了三个执⾏流共同完成任务,但本质上他们都是为了办理⼀家公司的业务。
        此时,我们就把这种情况称为多线程,将⼀个⼤任务分解成不同⼩任务,交给不同执⾏流就分别排队 执⾏。其中李四、王五都是张三叫来的,所以张三⼀般被称为主线程(Main Thread)。

2) 为啥要有线程

⾸先, "并发编程" 成为 "刚需".

  • 单核 CPU 的发展遇到了瓶颈. 要想提⾼算⼒, 就需要多核 CPU. ⽽并发编程能更充分利⽤多核 CPU资源.
  • 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发编程.

其次, 虽然多进程也能实现 并发编程, 但是线程⽐进程更轻量.(线程就是轻量级进程)

  • 创建线程⽐创建进程更快.
  • 销毁线程⽐销毁进程更快.
  • 调度线程⽐调度进程更快.

最后, 线程虽然⽐进程轻量, 但是⼈们还不满⾜, 于是⼜有了 "线程池"(ThreadPool) 和 "协程"(Coroutine)

3) 进程和线程的区别

  • 进程是包含线程的. 每个进程⾄少有⼀个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.
⽐如之前的多进程例⼦中,每个客⼾来银⾏办理各⾃的业务,但他们之间的票据肯定是不想让别⼈知道的,否则钱不就被其他⼈取⾛了么。⽽上⾯我们的公司业务中,张三、李四、王五虽然是不同的执⾏流,但因为办理的都是⼀家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最⼤区别。
  • 进程是系统分配资源的最⼩单位,线程是系统调度的最⼩单位。
  • ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带⾛(整个进程崩溃).

4) Java 的线程 和 操作系统线程 的关系

        线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对⽤⼾层提供了⼀些 API 供用户使⽤(例如 Linux 的 pthread 库).
api:application programming interface(应用程序编程接口)
  1. 操作系统提供的原生api是c写的
  2. 不同操作系统的线程api不相同
Java 标准库中 Thread 类 可以视为是对操作系统提供的 API 进⾏了进⼀步的抽象和封装.(Thread类就在Java默认导入的java.lang包里面)

2 第⼀个多线程程序

感受多线程程序和普通程序的区别:
  • 每个线程都是⼀个独⽴的执⾏流
  • 多个线程之间是 "并发" 执⾏的.
  使⽤ jconsole 命令观察线程

3 创建线程

⽅法1 继承 Thread 类

(1)继承 Thread 来创建⼀个线程类.
class MyThread extends Thread{
    @Override
    //run相当于线程的入口函数
    public void run() {
        System.out.println("hello world");
    }
}

(2)创建 MyThread 类的实例

Thread t=new MyThread();

(3)调⽤ start ⽅法启动线程

 //真正在系统中创建出一个线程
 t.start();

(4)休眠

 try {
       Thread.sleep(1000);//sleep是静态方法,表示休眠,休息一会再执行,用ms为单位
      } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

(5)run

        在执行线程时,不需要显示运行run方法,JVM自动调用

Tip:

在自己写的MyThread类里面不允许用throws,只能try-catch,因为其父类Thread里面没有实现该功能,但main函数中可以

实际开发中,异常的处理方式
1.记录异常信息作为日志.后续程序员根据日志调查问题
        程序仍然正常往后执行逻辑,不会因为这个异常就终止(对于服务器非常关键的)

2.进行重试,有的异常是概率性的(网络通讯)

3.特别严重的问题,必须立即马上处理的问题

        通过短信/邮件/微信/电话 通知程序员 (报警机制)


服务器和客户端指的是两个程序

服务器(server):被动接受请求,返回响应的一方

客户端(client):主动发起请求的一方

  • 客户端给服务器发送的数据,叫做“request"请求
  • 服务器给客户端返回的叫做“response”响应
  • 通常一个服务器可以给多个客户端提供服务
  • 服务器基本7*24待命

根据输出可以知道:多个线程的调度是随机的(“抢占式执行”)


Q:可以控制输出顺序吗?

A:输出顺序是操作系统内核的调度器控制的,没法在应用应用程序中编写代码控制 (调度器没有提供 api 的)
唯一能做的就是给线程设置优先级(但是优先级,对于操作系统来说,也是仅供参考,不会严格的定量的遵守)


如果直接使用run方法,而没有start,那么MyTread实质上没有创建出进程,只有main进程,遇到run中的死循环之后无法退出。

package Thread;
class MyThread extends Thread{
    @Override
    //run相当于线程的入口函数
    public void run() {
        while(true){
            System.out.println("hello run");
            try {
                Thread.sleep(1000);//sleep是静态方法,表示休眠,休息一会再执行,用ms为单位
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
public class demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new MyThread();
        //真正在系统中创建出一个线程
        //t.start();
        t.run();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

⽅法2 实现 Runnable 接⼝

1. 实现 Runnable 接⼝
class MyRunnable implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        }

}

2. 创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为 target 参数.

Runnable runnable=new MyRunnable();
Thread t=new Thread(runnable);

3. 调⽤ start ⽅法

 t.start();

总代码:

package Thread;
class MyRunnable implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        }

}

public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new MyRunnable();
        Thread t=new Thread(runnable);
        t.start();
        while (true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

Runnable最终还是要通过 Thread,真正创建线程

线程里要干啥, 通过 Runnable 来表示(而不是通过直接重写 Thread的run 来表示了)

Q:如何判断是用哪种?

A:根据线程要执行的任务的定义,是放到 Thread 里面,还是放到外面(Runnable 中)

Q:使用Runnable有什么好处吗?

A:解耦合。要执行的任务本身,和 线程这个概念,能够解耦合,从而后续如果变更代码.(比如不通过线程执行这个任务,通过其他方式.….)

采用 Runnable 这样的方案,代码的修改就会更简单.

对⽐上⾯两种⽅法:

  • 继承 Thread 类, 直接使⽤ this 就表⽰当前线程对象的引⽤.
  • 实现 Runnable 接⼝, this 表⽰的是 MyRunnable 的引⽤. 需要使⽤Thread.currentThread()

⽅法3 实现Tread的匿名内部类

package Thread;

public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(){
              while(true){
                System.out.println("hello run");
                try {
                    Thread.sleep(1000);//sleep是静态方法,表示休眠,休息一会再执行,用ms为单位
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        thread.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

⽅法4 匿名内部类创建 Runnable ⼦类对象

 
package Thread;

public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello run");
                    try {
                        Thread.sleep(1000);//sleep是静态方法,表示休眠,休息一会再执行,用ms为单位
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread thread=new Thread(runnable);
        thread.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

⽅法5  lambda 表达式创建 Runnable ⼦类对象

package Thread;

public class demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while (true) {
                System.out.println("hello run");
                try {
                    Thread.sleep(1000);//sleep是静态方法,表示休眠,休息一会再执行,用ms为单位
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

4 多线程的优势-增加运⾏速度

可以观察多线程在⼀些场合下是可以提⾼程序的整体运⾏效率的。
  • 使⽤ System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.
  • serial 串⾏的完成⼀系列运算.
  • concurrency 使⽤两个线程并⾏的完成同样的运算.
 

二. Thread 类及常⻅⽅法

      

        Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。

        ⽤我们上⾯的例⼦来看,每个执⾏流,也需要有⼀个对象来描述,类似下图所⽰,⽽ Thread 类的对象就是⽤来描述⼀个线程执⾏流的,JVM 会将这些 Thread 对象组织起来,⽤于线程调度,线程管理。

1 Thread 的常⻅构造⽅法

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

main方法结束了,主线程就结束了

以前认知里main方法结束,程序就执行完毕是针对单线程程序的

2 Thread 的⼏个常⻅属性

  • ID 是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  • 状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
  • 优先级⾼的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明
 
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还在");
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
        });
        System.out.println(Thread.currentThread().getName()
                + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName()
                + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName()
                + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName()
                + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName()
                + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName()
                + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName()
                + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {
        }
        System.out.println(Thread.currentThread().getName()
                + ": 状态: " + thread.getState());
    }
}

3 启动⼀个线程 - start()

之前我们已经看到了如何通过覆写 run ⽅法创建⼀个线程对象,但线程对象被创建出来并不意味着线程就开始运⾏了。

  • 覆写 run ⽅法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • ⽽调⽤ start() ⽅法,就是喊⼀声:”⾏动起来!“,线程才真正独⽴去执⾏了。

调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程

4 中断⼀个线程

        李四⼀旦进到⼯作状态,他就会按照⾏动指南上的步骤去进⾏⼯作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如⽼板突然来电话了,说转账的对⽅是个骗⼦,需要赶紧停⽌转账,那张三该如何通知李四停⽌呢?这就涉及到我们的停⽌线程的⽅式了。

⽬前常⻅的有以下两种⽅式:

  • 通过共享的标记来进⾏沟通
  • 调⽤ interrupt() ⽅法来通知

⽰例1: 使⽤⾃定义的变量来作为标志位.

//需要给标志位上加 volatile 关键字(这个关键字的功能后⾯介绍).
public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了⼤事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
        target.isQuit = true;
    }
}

⽰例-2: 使⽤ Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替⾃定义标志位.

Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记.

使⽤ thread 对象的 interrupted() ⽅法通知线程结束.
 
public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
        // 两种⽅法均可以
            while (!Thread.interrupted()) {
//while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName() + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName() + ": 有内⻤,终⽌交易!");
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 啊!险些误了⼤事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName() + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName() + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
        thread.interrupt();
    }
}

thread 收到通知的⽅式有两种:

1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通 知,清除中断标志

        ◦ 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.

          ◦ Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过这种⽅式通知收到的更及时,即使线程正在 sleep 也可以⻢上收到。

      

5 等待⼀个线程 - join()

有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要⼀个⽅法明确等待线程的结束。

package Thread;

public class ThreadDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Runnable target =() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName()
                            + ": 我还在⼯作!");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
        };

        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始⼯作");
        thread1.start();
        thread1.join();
        System.out.println("李四⼯作结束了,让王五开始⼯作");
        thread2.start();
        thread2.join();
        System.out.println("王五⼯作结束了");
    }
}

⼤家可以试试如果把两个 join 注释掉,现象会是怎么样的呢?

附录

6 获取当前线程引⽤

这个⽅法我们已经⾮常熟悉了

public class ThreadDemo {
    public static void main(String[] args) {
            Thread thread = Thread.currentThread();
             System.out.println(thread.getName());
          }
}

7 休眠当前线程

也是我们⽐较熟悉⼀组⽅法,有⼀点要记得,因为线程的调度是不可控的,所以,这个⽅法只能保证实际休眠时间是⼤于等于参数设置的休眠时间的。
 
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
    }
}

三、 线程的状态

1 观察线程的所有状态

线程的状态是⼀个枚举类型
 
Thread.State
public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}

  • NEW: 安排了⼯作, 还未开始⾏动
  • RUNNABLE: 可⼯作的. ⼜可以分成正在⼯作中和即将开始⼯作.
  • BLOCKED: 这⼏个都表⽰排队等着其他事情
  • WAITING: 这⼏个都表⽰排队等着其他事情
  • TIMED_WAITING: 这⼏个都表⽰排队等着其他事情
  • TERMINATED: ⼯作完成了.

2. 线程状态和状态转移的意义

⼤家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。

还是我们之前的例⼦:

刚把李四、王五找来,还是给他们在安排任务,没让他们⾏动起来,就是 NEW 状态;

当李四、王五开始去窗⼝排队,等待服务,就进⼊到 RUNNABLE 状态。该状态并不表⽰已经被银⾏⼯作⼈员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度;

当李四、王五因为⼀些事情需要去忙,例如需要填写信息、回家取证件、发呆⼀会等等时,进⼊BLOCKED 、 WATING 、TIMED_WAITING 状态,⾄于这些状态的细分,我们以后再详解;

如果李四、王五已经忙完,为 TERMINATED 状态。

所以,之前我们学过的 isAlive() ⽅法,可以认为是处于不是 NEW 和 TERMINATED 的状态都是活着的

3 观察线程的状态和转移

观察 1: 关注 NEW RUNNABLE TERMINATED 状态的转换  
package Thread;

public class ThreadStateTransfer {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 1000_0000; i++) {
            }
        }, "李四");
        System.out.println(t.getName() + ": " + t.getState());
        ;
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());
            ;
        }
        System.out.println(t.getName() + ": " + t.getState());
        ;
    }
}


观察 2: 关注 WAITING BLOCKED TIMED_WAITING 状态的转换
 public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }
}

使⽤ jconsole 可以看到 t1 的状态是 TIMED_WAITING , t2 的状态是 BLOCKED

修改上⾯的代码, 把 t1 中的 sleep 换成 wait
 
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    try {
            // [修改这⾥就可以了!!!!!]
            // Thread.sleep(1000);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "t1");
...
    }

使⽤ jconsole 可以看到 t1 的状态是 WAITING

结论:

BLOCKED 表⽰等待获取锁, WAITING 和 TIMED_WAITING 表⽰等待其他线程发来通知.

TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在⽆限等待唤醒

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

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

相关文章

设计模式期末复习

一、设计模式的概念以及分类 二、设计模式的主题和意图 设计模式的主题是关于软件设计中反复出现的问题以及相应的解决方案。这些主题是基于长期实践经验的总结&#xff0c;旨在提供一套可复用的设计思路和框架&#xff0c;以应对软件开发中的复杂性和变化性。 三、面向对象程…

【小白51单片机专用教程】protues仿真AT89C51入门

课程特点 无需开发板0基础教学软件硬件双修辅助入门 本课程面对纯小白&#xff0c;因此会对各个新出现的知识点在实例基础上进行详细讲解&#xff0c;有相关知识的可以直接跳过。课程涉及protues基本操作、原理图设计、数电模电、kell使用、C语言基本内容&#xff0c;所有涉及…

MFC用List Control 和Picture控件实现界面切换效果

添加List Control 和Picture控件 添加 3个子窗体 把子窗体边框设置为None, 样式设为Child 声明 CListCtrl m_listPageForm;void ShowForm(int nIndex);void CreatFormList();void CMFCApplication3Dlg::DoDataExchange(CDataExchange* pDX) {CDialogEx::DoDataExchange(pDX);DD…

Linux高并发服务器开发 第五天(压缩解压缩/vim编辑器)

目录 1.压缩和解压缩 1.1压缩 1.2解压缩 2.vim编辑器 2.1vim的3种工作模式 2.2切换编辑模式 2.3保存和退出 2.4光标移动 1.压缩和解压缩 - Linux 操作系统&#xff0c;默认支持的 压缩格式&#xff1a;gzip、bzip2。 默认&#xff0c;这两种压缩格式&#xff0c;只能…

接口测试Day-02-安装postman项目推送Gitee仓库

postman安装 下载 Postman&#xff08;已提供安装包&#xff0c;此步可以跳过&#xff09; https://www.postman.com/downloads/安装 Postman 安装Postman插件newman 要想给 postman 安装 newman 插件&#xff0c;必须 先 安装 node.js。 这是前提&#xff01; 安装node.js 可能…

虚拟地址空间 -- 虚拟地址,虚拟内存管理

1. C/C语言的内存空间分布 用下列代码来观察各种区域的地址&#xff1a; #include <stdio.h> #include <unistd.h> #include <stdlib.h>int g_unval; int g_val 100;int main(int argc, char *argv[], char *env[]) {const char *str "helloworld&qu…

【数字化】华为数字化转型架构蓝图-2

目录 1、客户联结的架构思路 1.1 ROADS体验设计 1.2 具体应用场景 1.3 统一的数据底座 1.4 案例与成效 2、一线作战平台的架构思路 2.1 核心要素 2.2 关键功能 2.3 实施路径 2.4 案例与成效 3、能力数字化的架构思路 3.1 能力数字化的核心目标 3.2 能力数字化的实…

【优选算法】—移动零(双指针算法)

云边有个稻草人-CSDN博客 想当一名牛的程序员怎么能少的了练习算法呢&#xff1f;&#xff01; 今天就立即开启一个新专栏&#xff0c;专干算法&#xff0c;提高算法能力&#xff08;废柴的我也在准备蓝桥杯哈哈&#xff09;—— 目录 1.【 283. 移动零 - 力扣&#xff08;Lee…

AI的进阶之路:从机器学习到深度学习的演变(三)

&#xff08;承接上集&#xff1a;AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;二&#xff09;&#xff09; 四、深度学习&#xff08;DL&#xff09;&#xff1a;机器学习的革命性突破 深度学习&#xff08;DL&#xff09;作为机器学习的一个重要分支&am…

Python自动化测试:线上流量回放

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在自动化测试中&#xff0c;线上流量回放是一项关键技术&#xff0c;可以模拟真实用户的请求并重现线上场景&#xff0c;验证系统的性能和稳定性。本文将介绍Pytho…

初始C语言3

目录 9. 操作符 9.1 算术操作符 9.2 移位操作符 9.3 位操作符 9.4 赋值操作符 9.5 单目操作符 9.6 关系操作符 9.7 逻辑操作符 9.8 条件操作符 9.9 逗号表达式 下标引用、函数调用和结构成员 10. 常见关键字 10.1 typedef 10.2 static 10.2.1 修饰局部变量 10.…

【Rust自学】4.5. 切片(Slice)

4.5.0. 写在正文之前 这是第四章的最后一篇文章了&#xff0c;在这里也顺便对这章做一个总结&#xff1a; 所有权、借用和切片的概念确保 Rust 程序在编译时的内存安全。 Rust语言让程序员能够以与其他系统编程语言相同的方式控制内存使用情况&#xff0c;但是当数据所有者超…

VPN技术-GRE隧道的配置

GRE隧道的配置 1&#xff0c; 在AR1上配置DHCP接口地址池&#xff0c;AR3上配置DHCP全局地址池 2&#xff0c; PC1获取的IP地址为10.10.10.253&#xff0c;PC2获取的IP地址为10.10.30.253 3&#xff0c;通过ip route-static将目的地址为10.10.30.253的流量引入到Tunnel #配…

碰撞检测算法之闵可夫斯基差集法(Minkowski Difference)

在游戏开发和机器人路径规划乃至于现在比较火的自动驾驶中&#xff0c;我们常常需要确定两个物体是否发生碰撞&#xff0c;有一种通过闵可夫斯基差集法求是否相交的算法&#xff0c;下面将介绍一下 闵可夫斯基差集法的优势 闵可夫斯基差集法优势&#xff1a; 可以处理复杂的…

Python OCR 文字识别

一.引言 文字识别&#xff0c;也称为光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;是一种将不同形式的文档&#xff08;如扫描的纸质文档、PDF文件或数字相机拍摄的图片&#xff09;中的文字转换成可编辑和可搜索的数据的技术。随着技…

【系统】Windows11更新解决办法,一键暂停

最近的windows更新整的我是措不及防&#xff0c;干啥都要关注一下更新的问题&#xff0c;有的时候还关不掉&#xff0c;我的强迫症就来了&#xff0c;非得关了你不可&#xff01; 经过了九九八十一难的研究之后&#xff0c;终于找到了一个算是比较靠谱的暂停更新的方法&#x…

复合翼与倾转旋翼飞行器:设计与控制算法对比

一、引言 复合翼&#xff08;Compound Wing&#xff09;和倾转旋翼&#xff08;Tilt - Rotor&#xff09;飞行器在现代航空领域均占据独特地位&#xff0c;二者在设计和控制算法方面展现出显著差异。这些差异在飞行模式切换、推进系统设计、控制算法复杂度以及飞行器稳定性等多…

空闲中断配合DMA

1.传统串口接收数据&#xff1a;来一个字节接受一个。 2.一次中断将一包数据存到缓冲区 3.DMA原理

三格电子——新品IE103转ModbusTCP网关

型号&#xff1a;SG-TCP-IEC103 产品概述 IE103转ModbusTCP网关型号SG-TCP-IEC103&#xff0c;是三格电子推出的工业级网关&#xff08;以下简称网关&#xff09;&#xff0c;主要用于IEC103数据采集、DLT645-1997/2007数据采集&#xff0c;IEC103支持遥测和遥信&#xff0c;可…

HDLBits训练3

时间&#xff1a;2024.12.22 Hadd 代码 法一&#xff1a; module top_module( input a, b,output cout, sum );assign {cout,sum}ab; endmodule法二&#xff1a; 运行结果 Fadd 代码 法一&#xff1a; module top_module( input a, b, cin,output cout, sum );assign…