juc系列(2)--线程的使用及原理

news2024/9/20 10:46:50

目录

    • 线程
      • 创建线程
        • Thread
        • Runnable
        • Callable
      • 线程方法
        • API
        • run start
        • sleep yield
        • join
        • interrupt
          • 打断线程
          • 打断 park
          • 终止模式
        • daemon
        • 不推荐
      • 线程原理
        • 运行机制
        • 线程调度
        • 未来优化
      • 线程状态
      • 查看线程

线程

创建线程

Thread

Thread 创建线程方式:创建线程类,匿名内部类方式

  • start() 方法底层其实是给 CPU 注册当前线程,并且触发 run() 方法执行
  • 线程的启动必须调用 start() 方法,如果线程直接调用 run() 方法,相当于变成了普通类的执行,此时主线程将只有执行该线程
  • 建议线程先创建子线程,主线程的任务放在之后,否则主线程(main)永远是先执行完

Thread 构造器:

  • public Thread()
  • public Thread(String name)
public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
       	for(int i = 0 ; i < 100 ; i++ ){
            System.out.println("main线程" + i)
        }
        // main线程输出放在上面 就变成有先后顺序了,因为是 main 线程驱动的子线程运行
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        for(int i = 0 ; i < 100 ; i++ ) {
            System.out.println("子线程输出:"+i)
        }
    }
}

继承 Thread 类的优缺点:

  • 优点:编码简单
  • 缺点:线程类已经继承了 Thread 类无法继承其他类了,功能不能通过继承拓展(单继承的局限性)

Runnable

Runnable 创建线程方式:创建线程类,匿名内部类方式

Thread 的构造器:

  • public Thread(Runnable target)
  • public Thread(Runnable target, String name)
public class ThreadDemo {
    public static void main(String[] args) {
        Runnable target = new MyRunnable();
        Thread t1 = new Thread(target,"1号线程");
		t1.start();
        Thread t2 = new Thread(target);//Thread-0
    }
}

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }
    }
}

Thread 类本身也是实现了 Runnable 接口,Thread 类中持有 Runnable 的属性,执行线程 run 方法底层是调用 Runnable#run:

public class Thread implements Runnable {
    private Runnable target;
    
    public void run() {
        if (target != null) {
          	// 底层调用的是 Runnable 的 run 方法
            target.run();
        }
    }
}

Runnable 方式的优缺点:

  • 缺点:代码复杂一点。

  • 优点:

    1. 线程任务类只是实现了 Runnable 接口,可以继续继承其他类,避免了单继承的局限性

    2. 同一个线程任务对象可以被包装成多个线程对象

    3. 适合多个多个线程去共享同一个资源

    4. 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立

    5. 线程池可以放入实现 Runnable 或 Callable 线程任务对象


Callable

实现 Callable 接口:

  1. 定义一个线程任务类实现 Callable 接口,申明线程执行的结果类型
  2. 重写线程任务类的 call 方法,这个方法可以直接返回执行的结果
  3. 创建一个 Callable 的线程任务对象
  4. 把 Callable 的线程任务对象包装成一个未来任务对象
  5. 把未来任务对象包装成线程对象
  6. 调用线程的 start() 方法启动线程

public FutureTask(Callable<V> callable):未来任务对象,在线程执行完后得到线程的执行结果

  • FutureTask 就是 Runnable 对象,因为 Thread 类只能执行 Runnable 实例的任务对象,所以把 Callable 包装成未来任务对象
  • 线程池部分详解了 FutureTask 的源码

public V get():同步等待 task 执行完毕的结果,如果在线程中获取另一个线程执行结果,会阻塞等待,用于线程同步

  • get() 线程会阻塞等待任务执行完成
  • run() 执行完后会把结果设置到 FutureTask 的一个成员变量,get() 线程可以获取到该变量的值

优缺点:

  • 优点:同 Runnable,并且能得到线程执行的结果
  • 缺点:编码复杂
public class ThreadDemo {
    public static void main(String[] args) {
        Callable call = new MyCallable();
        FutureTask<String> task = new FutureTask<>(call);
        Thread t = new Thread(task);
        t.start();
        try {
            String s = task.get(); // 获取call方法返回的结果(正常/异常结果)
            System.out.println(s);
        }  catch (Exception e) {
            e.printStackTrace();
        }
    }

public class MyCallable implements Callable<String> {
    @Override//重写线程任务类方法
    public String call() throws Exception {
        return Thread.currentThread().getName() + "->" + "Hello World";
    }
}

线程方法

API

Thread 类 API:

方法说明
public void start()启动一个新线程,Java虚拟机调用此线程的 run 方法
public void run()线程启动后调用该方法
public void setName(String name)给当前线程取名字
public void getName()获取当前线程的名字
线程存在默认名称:子线程是 Thread-索引,主线程是 main
public static Thread currentThread()获取当前线程对象,代码在哪个线程中执行
public static void sleep(long time)让当前线程休眠多少毫秒再继续执行
Thread.sleep(0) : 让操作系统立刻重新进行一次 CPU 竞争
public static native void yield()提示线程调度器让出当前线程对 CPU 的使用
public final int getPriority()返回此线程的优先级
public final void setPriority(int priority)更改此线程的优先级,常用 1 5 10
public void interrupt()中断这个线程,异常处理机制
public static boolean interrupted()判断当前线程是否被打断,清除打断标记
public boolean isInterrupted()判断当前线程是否被打断,不清除打断标记
public final void join()等待这个线程结束
public final void join(long millis)等待这个线程死亡 millis 毫秒,0 意味着永远等待
public final native boolean isAlive()线程是否存活(还没有运行完毕)
public final void setDaemon(boolean on)将此线程标记为守护线程或用户线程

run start

run:称为线程体,包含了要执行的这个线程的内容,方法运行结束,此线程随即终止。直接调用 run 是在主线程中执行了 run,没有启动新的线程,需要顺序执行

start:使用 start 是启动新的线程,此线程处于就绪(可运行)状态,通过新的线程间接执行 run 中的代码

说明:线程控制资源类

run() 方法中的异常不能抛出,只能 try/catch

  • 因为父类中没有抛出任何异常,子类不能比父类抛出更多的异常
  • 异常不能跨线程传播回 main() 中,因此必须在本地进行处理

sleep yield

sleep:

  • 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  • sleep() 方法的过程中,线程不会释放对象锁
  • 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  • 睡眠结束后的线程未必会立刻得到执行,需要抢占 CPU
  • 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield:

  • 调用 yield 会让提示线程调度器让出当前线程对 CPU 的使用
  • 具体的实现依赖于操作系统的任务调度器
  • 会放弃 CPU 资源,锁资源不会释放

join

public final void join():等待这个线程结束

原理:调用者轮询检查线程 alive 状态,t1.join() 等价于:

public final synchronized void join(long millis) throws InterruptedException {
    // 调用者线程进入 thread 的 waitSet 等待, 直到当前线程运行结束
    while (isAlive()) {
        wait(0);
    }
}
  • join 方法是被 synchronized 修饰的,本质上是一个对象锁,其内部的 wait 方法调用也是释放锁的,但是释放的是当前的线程对象锁,而不是外面的锁

  • 当调用某个线程(t1)的 join 方法后,该线程(t1)抢占到 CPU 资源,就不再释放,直到线程执行完毕

线程同步:

  • join 实现线程同步,因为会阻塞等待另一个线程的结束,才能继续向下运行
    • 需要外部共享变量,不符合面向对象封装的思想
    • 必须等待线程结束,不能配合线程池使用
  • Future 实现(同步):get() 方法阻塞等待执行结果
    • main 线程接收结果
    • get 方法是让调用线程同步等待
public class Test {
    static int r = 0;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    private static void test1() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r = 10;
        });
        t1.start();
        t1.join();//不等待线程执行结束,输出的10
        System.out.println(r);
    }
}

interrupt

打断线程

public void interrupt():打断这个线程,异常处理机制

public static boolean interrupted():判断当前线程是否被打断,打断返回 true,清除打断标记,连续调用两次一定返回 false

public boolean isInterrupted():判断当前线程是否被打断,不清除打断标记

打断的线程会发生上下文切换,操作系统会保存线程信息,抢占到 CPU 后会从中断的地方接着运行(打断不是停止)

  • sleep、wait、join 方法都会让线程进入阻塞状态,打断线程会清空打断状态(false)

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
        System.out.println(" 打断状态: {}" + t1.isInterrupted());// 打断状态: {}false
    }
    
  • 打断正常运行的线程:不会清空打断状态(true)

    public static void main(String[] args) throws Exception {
        Thread t2 = new Thread(()->{
            while(true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if(interrupted) {
                    System.out.println(" 打断状态: {}" + interrupted);//打断状态: {}true
                    break;
                }
            }
        }, "t2");
        t2.start();
        Thread.sleep(500);
        t2.interrupt();
    }
    

打断 park

park 作用类似 sleep,打断 park 线程,不会清空打断状态(true)

public static void main(String[] args) throws Exception {
    Thread t1 = new Thread(() -> {
        System.out.println("park...");
        LockSupport.park();
        System.out.println("unpark...");
        System.out.println("打断状态:" + Thread.currentThread().isInterrupted());//打断状态:true
    }, "t1");
    t1.start();
    Thread.sleep(2000);
    t1.interrupt();
}

如果打断标记已经是 true, 则 park 会失效

LockSupport.park();
System.out.println("unpark...");
LockSupport.park();//失效,不会阻塞
System.out.println("unpark...");//和上一个unpark同时执行

可以修改获取打断状态方法,使用 Thread.interrupted(),清除打断标记

LockSupport 类在 同步 → park-un 详解


终止模式

终止模式之两阶段终止模式:Two Phase Termination

目标:在一个线程 T1 中如何优雅终止线程 T2?优雅指的是给 T2 一个后置处理器

错误思想:

  • 使用线程对象的 stop() 方法停止线程:stop 方法会真正杀死线程,如果这时线程锁住了共享资源,当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
  • 使用 System.exit(int) 方法停止线程:目的仅是停止一个线程,但这种做法会让整个程序都停止

两阶段终止模式图示:

打断线程可能在任何时间,所以需要考虑在任何时刻被打断的处理方法:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}
class TwoPhaseTermination {
    private Thread monitor;
    // 启动监控线程
    public void start() {
        monitor = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Thread thread = Thread.currentThread();
                    if (thread.isInterrupted()) {
                        System.out.println("后置处理");
                        break;
                    }
                    try {
                        Thread.sleep(1000);					// 睡眠
                        System.out.println("执行监控记录");	// 在此被打断不会异常
                    } catch (InterruptedException e) {		// 在睡眠期间被打断,进入异常处理的逻辑
                        e.printStackTrace();
                        // 重新设置打断标记,打断 sleep 会清除打断状态
                        thread.interrupt();
                    }
                }
            }
        });
        monitor.start();
    }
    // 停止监控线程
    public void stop() {
        monitor.interrupt();
    }
}

daemon

public final void setDaemon(boolean on):如果是 true ,将此线程标记为守护线程

线程启动前调用此方法:

Thread t = new Thread() {
    @Override
    public void run() {
        System.out.println("running");
    }
};
// 设置该线程为守护线程
t.setDaemon(true);
t.start();

用户线程:平常创建的普通线程

守护线程:服务于用户线程,只要其它非守护线程运行结束了,即使守护线程代码没有执行完,也会强制结束。守护进程是脱离于终端并且在后台运行的进程,脱离终端是为了避免在执行的过程中的信息在终端上显示

说明:当运行的线程都是守护线程,Java 虚拟机将退出,因为普通线程执行完后,JVM 是守护线程,不会继续运行下去

常见的守护线程:

  • 垃圾回收器线程就是一种守护线程
  • Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

不推荐

不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁:

  • public final void stop():停止线程运行

    废弃原因:方法粗暴,除非可能执行 finally 代码块以及释放 synchronized 外,线程将直接被终止,如果线程持有 JUC 的互斥锁可能导致锁来不及释放,造成其他线程永远等待的局面

  • public final void suspend()挂起(暂停)线程运行

    废弃原因:如果目标线程在暂停时对系统资源持有锁,则在目标线程恢复之前没有线程可以访问该资源,如果恢复目标线程的线程在调用 resume 之前会尝试访问此共享资源,则会导致死锁

  • public final void resume():恢复线程运行


线程原理

运行机制

Java Virtual Machine Stacks(Java 虚拟机栈):每个线程启动后,虚拟机就会为其分配一块栈内存

  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

线程上下文切换(Thread Context Switch):一些原因导致 CPU 不再执行当前线程,转而执行另一个线程

  • 线程的 CPU 时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park 等方法

程序计数器(Program Counter Register):记住下一条 JVM 指令的执行地址,是线程私有的

当 Context Switch 发生时,需要由操作系统保存当前线程的状态(PCB 中),并恢复另一个线程的状态,包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等

JVM 规范并没有限定线程模型,以 HotSopot 为例:

  • Java 的线程是内核级线程(1:1 线程模型),每个 Java 线程都映射到一个操作系统原生线程,需要消耗一定的内核资源(堆栈)
  • 线程的调度是在内核态运行的,而线程中的代码是在用户态运行,所以线程切换(状态改变)会导致用户与内核态转换进行系统调用,这是非常消耗性能

Java 中 main 方法启动的是一个进程也是一个主线程,main 方法里面的其他线程均为子线程,main 线程是这些线程的父线程


线程调度

线程调度指系统为线程分配处理器使用权的过程,方式有两种:协同式线程调度、抢占式线程调度(Java 选择)

协同式线程调度:线程的执行时间由线程本身控制

  • 优点:线程做完任务才通知系统切换到其他线程,相当于所有线程串行执行,不会出现线程同步问题
  • 缺点:线程执行时间不可控,如果代码编写出现问题,可能导致程序一直阻塞,引起系统的奔溃

抢占式线程调度:线程的执行时间由系统分配

  • 优点:线程执行时间可控,不会因为一个线程的问题而导致整体系统不可用
  • 缺点:无法主动为某个线程多分配时间

Java 提供了线程优先级的机制,优先级会提示(hint)调度器优先调度该线程,但这仅仅是一个提示,调度器可以忽略它。在线程的就绪状态时,如果 CPU 比较忙,那么优先级高的线程会获得更多的时间片,但 CPU 闲时,优先级几乎没作用

说明:并不能通过优先级来判断线程执行的先后顺序


未来优化

内核级线程调度的成本较大,所以引入了更轻量级的协程。用户线程的调度由用户自己实现(多对一的线程模型,多个用户线程映射到一个内核级线程),被设计为协同式调度,所以叫协程

  • 有栈协程:协程会完整的做调用栈的保护、恢复工作,所以叫有栈协程
  • 无栈协程:本质上是一种有限状态机,状态保存在闭包里,比有栈协程更轻量,但是功能有限

有栈协程中有一种特例叫纤程,在新并发模型中,一段纤程的代码被分为两部分,执行过程和调度器:

  • 执行过程:用于维护执行现场,保护、恢复上下文状态
  • 调度器:负责编排所有要执行的代码顺序

线程状态

进程的状态参考操作系统:创建态、就绪态、运行态、阻塞态、终止态

线程由生到死的完整过程(生命周期):当线程被创建并启动以后,既不是一启动就进入了执行状态,也不是一直处于执行状态,在 API 中 java.lang.Thread.State 这个枚举中给出了六种线程状态:

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动,还没调用 start 方法,只有线程对象,没有线程特征
Runnable(可运行)线程可以在 Java 虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器,调用了 t.start() 方法:就绪(经典叫法)
Blocked(阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态;当该线程持有锁时,该线程将变成 Runnable 状态
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态,进入这个状态后不能自动唤醒,必须等待另一个线程调用 notify 或者 notifyAll 方法才能唤醒
Timed Waiting (限期等待)有几个方法有超时参数,调用将进入 Timed Waiting 状态,这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait
Teminated(结束)run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7iFiq1QA-1673836912402)(https://seazean.oss-cn-beijing.aliyuncs.com/img/Java/JUC-线程6种状态.png)]

  • NEW → RUNNABLE:当调用 t.start() 方法时,由 NEW → RUNNABLE

  • RUNNABLE <–> WAITING:

    • 调用 obj.wait() 方法时

      调用 obj.notify()、obj.notifyAll()、t.interrupt():

      • 竞争锁成功,t 线程从 WAITING → RUNNABLE
      • 竞争锁失败,t 线程从 WAITING → BLOCKED
    • 当前线程调用 t.join() 方法,注意是当前线程在 t 线程对象的监视器上等待

    • 当前线程调用 LockSupport.park() 方法

  • RUNNABLE <–> TIMED_WAITING:调用 obj.wait(long n) 方法、当前线程调用 t.join(long n) 方法、当前线程调用 Thread.sleep(long n)

  • RUNNABLE <–> BLOCKED:t 线程用 synchronized(obj) 获取了对象锁时竞争失败


查看线程

Windows:

  • 任务管理器可以查看进程和线程数,也可以用来杀死进程
  • tasklist 查看进程
  • taskkill 杀死进程

Linux:

  • ps -ef 查看所有进程
  • ps -fT -p 查看某个进程(PID)的所有线程
  • kill 杀死进程
  • top 按大写 H 切换是否显示线程
  • top -H -p 查看某个进程(PID)的所有线程

Java:

  • jps 命令查看所有 Java 进程
  • jstack 查看某个 Java 进程(PID)的所有线程状态
  • jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)

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

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

相关文章

R-P-Faster R-CNN day65 读论文:高分辨率遥感影像综合地理空间目标检测框架

An Efficient and Robust Integrated Geospatial Object Detection Framework for High Spatial Resolution Remote Sensing Imagery 1. Introduction3. Overview of the Proposed R-P-Faster R-CNN Framework3.1. 有效集成区域建议网络与目标检测Faster R-CNN框架3.1.2. RPN与…

java反射在spring ioc和aop中的应用

java反射在spring ioc和aop中的应用 反射&#xff1a; 1.反射是什么&#xff1f; 程序运行时&#xff0c;通过类名能够获得类的属性和方法。使用方式如下 Class clazz Class.ForName(“Student”)Class clazz Student.class;Class clazz student.getClass(); 获取到claz…

java JUC 中 Object里wait()、notify() 实现原理及实战讲解

1.Object中的wait()实现原理 在进行wait()之前&#xff0c;就代表着需要争夺Synchorized&#xff0c;而Synchronized代码块通过javap生成的字节码中包含monitorenter和monitorexit两个指令。 当在进加锁的时候会执行monitorenter指令&#xff0c;执行该指令可以获取对象的mon…

前端与HTML

本节课程围绕“前端要解决的基本问题”及“什么是 HTML ”两个基本问题展开&#xff0c;了解 HTML 高效的编写原则。 什么是前端 使用web技术栈解决多端的人机交互问题 技术栈 html&#xff08;内容&#xff09; css &#xff08;样式&#xff09;javascript &#xff08;行…

linux部署KubeSphere和k8s集群

上一篇文章讲述了在单个节点上安装 KubeSphere和k8s&#xff0c;这节主要讲解k8s多节点集群部署 准备环境&#xff1a;Alibaba Cloud Linux系统3台机器第一步&#xff1a;设置主机名称hostname--(3台机器都设置) hostnamectl set-hostname master hostnamectl set-hostname nod…

智云通CRM:为什么你总是在请客,但业绩却上不来?

王总是一位企业老板&#xff0c;社会资源比较好&#xff0c;在过去的一年里&#xff0c;他新代理的一个保健品的项目&#xff0c;需要销售产品和招募合伙人。他想利用自己的人脉资源做销售&#xff0c;但他的销售过程并不顺利&#xff0c;在连续主动邀约之后效果不佳。 于是他…

2023/1/15 JS-变量提升与函数提升 执行上下文

1 变量提升与函数提升 变量声明提升 通过 var 声明的变量&#xff0c;在声明语句之前就可以访问到 - 值: undefined <script>console.log(a); // undefinedvar a 10 </script>函数声明提升 通过 function 声明的函数, 在声明语句之前就可以直接调用 - 值: 函数…

走近软件生态系统

生态系统&#xff08;Ecosystem&#xff09;原本是一个生物学术语&#xff0c;意思是由一些生命体相互依存、相互制约而形成的大系统&#xff0c;就像我们学生时代在生物学课堂上学到的那样。隐喻无处不在&#xff0c;人们把这个术语移植到了 IT 领域中来&#xff0c;比如我们常…

计算机基础(六):静态链接与动态链接

上一篇文章简单概括了 C语言程序经过编译&#xff0c;生成汇编语言、机器语言的基本过程。今天主要介绍其中链接阶段的实现思路。 静态链接 静态链接是将被依赖的代码片段复制到执行程序中&#xff0c;进行代码整合。因为我们在汇编代码中看到的是具体的符号&#xff0c;而且…

电路方案分析(十七)TI远程声控参考设计

远程声控参考设计 描述 CC2650远程控制设计为基于ZigBeeRF4CE™兼容的软件架构RemeTI™或蓝牙低能耗软件堆栈的快速测试、评估和开发远程控制应用程序提供了最佳基础。 该方案设计包含了CC2560远程控制的原理图和布局文件&#xff0c;以及一个演示了使用RF4CE和低能耗蓝牙的…

层次分析法和熵值法经典实操案例+数据

1、数据来源&#xff1a;无 2、时间跨度&#xff1a;无 3、区域范围&#xff1a;无 4、指标说明&#xff1a; 层次分析法&#xff08;Analytic Hierarchy Process&#xff0c;简称AHP&#xff09;是美国运筹学家、匹兹堡大学T. L. Saaty教授在20世纪70年代初期提出的&#…

《Buildozer打包实战指南》第二节 安装Kivy和Buildozer

目录 2.1 安装Kivy 2.2 安装Buildozer 2.3 验证安装 2.4 一点建议 Python是Ubuntu系统中自带的&#xff0c;我们在桌面上右键打开终端&#xff0c;然后输入python3 --version就可以看到Ubuntu系统中的Python版本了。 可以看到&#xff0c;Python的版本是3.10.6。虽然Python…

【Go基础】结构体

1. 结构体引入 Golang也支持面向对象编程&#xff0c;但是和传统的面向对象有区别&#xff0c;并不是像Java、C那样纯粹的面向对象语言&#xff0c;而是通过特别的手段实现面向对象特点。 Golang没有类(Class)的概念&#xff0c;但是提供了结构体(struct)&#xff0c;和其他编…

Nacos的学习

Nacos的学习 1、下载地址 https://github.com/alibaba/nacos/releases/tag/2.1.1 在bin目录中输入命令 startup.cmd -m standalone 输入localhost:8848/nacos 账号&#xff1a;nacos&#xff0c;密码&#xff1a;nacos 2、Spring与Nacos &#xff08;1&#xff09;新增一个配…

100天精通Python(数据分析篇)——第72天:Pandas文本数据处理方法之判断类型、去除空白字符、拆分和连接

文章目录每篇前言一、Python字符串内置方法1. 判断类型2. 去除空白字符3. 拆分和连接二、Pandas判断类型1. str.isspace()2. str.isalnum()3. str.isalpha()4. str.isdecimal()5. str.isdigit()6. str.isnumeric()7. str.istitle()8. str.islower()9. str.isupper()三、Pandas去…

音视频技术开发周刊 | 279

每周一期&#xff0c;纵览音视频技术领域的干货。新闻投稿&#xff1a;contributelivevideostack.com。基于NeRF的APP上架苹果商店&#xff01;照片转3D只需一部手机这个名叫Luma AI的“NeRF APP”&#xff0c;正式上架App Store后爆火。反 AiArt 运动中两件匪夷所思的蠢事Redd…

Elastic:使用 Postman 来访问

Elastic&#xff1a;使用 Postman 来访问 学习资料 Elastic&#xff1a;使用 Postman 来访问 Elastic Stack 当我们配置好elasticsearch的SSL之后&#xff0c;我们用网页https访问&#xff0c;输入账户及密码之后&#xff0c;可以成功访问数据。 但是用postman时&#xff0c;我…

2023/1/15 JS-闭包问题研究

1 举个栗子分析执行上下文 1: let a 3 2: function addTwo(x) { 3: let ret x 2 4: return ret 5: } 6: let b addTwo(a) 7: console.log(b)为了理解 JavaScript 引擎是如何工作的&#xff0c;让我们详细分析一下&#xff1a; 在第 1 行&#xff0c;我们在全局执行上…

Linux chattr命令

Linux chattr命令Linux 命令大全Linux chattr命令用于改变文件属性。这项指令可改变存放在ext2文件系统上的文件或目录属性&#xff0c;这些属性共有以下8种模式&#xff1a;a&#xff1a;让文件或目录仅供附加用途。b&#xff1a;不更新文件或目录的最后存取时间。c&#xff1…

从上到下看内存

从上到下看内存 1. 本篇目录 内存条,总线,DMAC 内存管理内存分类 内存相关的系统调用 java中的内存 2. 内存条,总线,DMAC 内存条 内存条&#xff1a;内存条其实是非常常见的一个组件。内存条是插在主板上的。 总线 内存条插好以后&#xff0c;计算机之间要进行交互。…