Java:多线程(进程线程、线程状态、创建线程、线程操作)

news2024/11/14 19:10:53

1,线程概述

1.1,进程和线程

并发是指系统能够同时处理多个任务或操作,通常通过在单个处理器或多个处理器之间快速切换上下文来实现。这些任务可能不是同时进行的,但是它们在时间上重叠。

并行是指系统同时执行多个任务或操作,通常通过在多个处理器上同时执行不同的任务来实现。这些任务是同时进行的,因此可以更快地完成。

进程:进程是系统中运行的一个程序,程序一旦运行就是进程。进程是系统分配资源的实体,每个进程都有独立的地址空间。一个进程无法访问另一个进程的变量和数据机构如果想让一个进程访问另一个进程的资源,需要使用进程间通信,比如管道,文件,套接字。

线程:线程是进程的进一步划分,是CPU调度和分派的最小单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

协程:协程是一种程序组件,是一种比线程更加轻量级的存在。正如一个进程可以有多个线程,一个线程可以有多个协程。协程是用户视角的一种抽象,操作系统并没有协程的概念。协程运行在线程之上,协程的主要思想是在用户态实现调度算法,用少量线程完成大量任务的调度。多进程、多线程已经提高了系统的并发能力,但是在当今互联网高并发场景下,为每个任务都创建一个线程是不现实的,因为会消耗大量的内存 (进程虚拟内存会占用 4GB [32 位操作系统],而线程也要大约 4MB)。

【内存空间共享问题】

  • 多线程进程内部的所有内存都由所有线程"共享"。这通常是线程的定义,因为它们都在同一内存空间中运行。
  • 当从第一个进程派生出另一个进程时,它们将共享相同的内存空间。如果某个进程尚未从另一个进程派生,则它们通常不共享任何内存(通信除外)。

【协程的优势】

  • 内存占用要小,且创建开销要小:用户态的协程,可以设计的很小,可以达到 KB 级别。是线程的千分之一。线程栈空间通常是MB级别, 协程栈空间最小KB级别。
  • 减少上下文切换的开销:让可执行的线程尽量少,这样切换次数必然会少;让线程尽可能的处于运行状态,而不是阻塞让出时间片,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上(分时复用)。即使有协程阻塞,该线程的其他协程也可以被 runtime 调度,转移到其他可运行的线程上。

1.2,多线程优势

当操作系统创建一个进程时,必须为该进程分配独立的内存空间,并分配大量的相关资源;但创建一个线程则简单得多,因此使用多线程来实现并发比使用多线程实现并发的性能要高很多。

  • 进程之间不能共享内存,但线程之间共享内存非常容易。
  • 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。
  • Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调用方式,从而简化了Java的多线程编程。

【应用场景】

  • 并发请求处理:在高并发的情况下,使用多线程可以让应用程序同时处理多个请求,提高系统的吞吐量和响应速度。

  • 图像处理:在图像处理和计算机视觉应用中,多线程可以加速对图像的处理和分析,提高应用程序的实时性和准确性。

  • 游戏开发:在游戏开发中,多线程可以处理不同的游戏逻辑、渲染和音效,提高游戏的流畅性和效果。

  • Web服务器:在Web服务器中,多线程可以处理多个用户请求,提高Web应用程序的并发能力和响应速度。

1.3,守护线程

守护线程(默认情况下创建的都是非守护线程)是一种在后台运行的线程,它不会阻止程序的结束,即使所有的非守护线程都已经结束,它也会自动退出。与之相反,非守护线程在主线程结束之前必须完成它们的任务,否则程序将一直等待它们结束。 

2,线程的状态和生命周期

2.1,线程的生命周期

  • suspend():暂时挂起线程。
  • resume():恢复挂起的线程。
  • stop():停止线程。

2.2,线程的状态

  • 创建状态:在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread类的构造方法,例如“Thread thread = new Thread();”。
  • 就绪状态:新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU服务,这表明线程已经具备了运行条件。
  • 运行状态:当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。
  • 堵塞状态:一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作时,会让除CPU并暂时中止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep(),suspend(),wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被消除后,线程才可以转入就绪状态。
  • 死亡状态:线程调用stop()方法或run()方法执行结束后,即处于死亡状态。处于死亡状态的线程不具备继续运行的能力。

3,线程的创建

创建线程有三种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口。

3.1,继承Thread类

通过继承Thread类来创建并启动线程的步骤如下:

  • 定义Thread类的子类,并重写该类的run()方法,该run()方法将作为线程执行体。
  • 创建Thread子类的实例,即创建了线程对象。
  • 调用线程对象的start()方法来启动该线程。
class 类名 extends Thread{
    属性...;
    方法...;
    public void run(){
        线程主体;
    }
}

在Thread子类中,必须明确地覆盖Thread类中的run()方法,此方法为线程的主体。 

class MyThread extends Thread {
    private String name;
    public MyThread(String name) {this.name = name;}
    public void run() {
        for (int i = 0; i < 5; i++) {System.out.println("i = " + i);}
    }
}
public class HelloWord {
    public static void main(String args[]) {
        MyThread thread1 = new MyThread("线程 A");
        MyThread thread2 = new MyThread("线程 B");
        thread1.run();thread2.run();
    }
}
======================================================================
name = 线程 A,i = 0
name = 线程 A,i = 1
name = 线程 B,i = 0
name = 线程 B,i = 1

【问题】为啥仍然是顺序执行呢?其实此时线程实际上并没有被启动,还是属于顺序式的执行方式,那么如何启动线程呢?如果要正确地启动线程,是不能直接调用run()方法的,而应该是调用从Thread类中继承而来的start()方法。

thread1.start();thread2.start();
================================
name = 线程 A,i = 0
name = 线程 B,i = 0
name = 线程 A,i = 1
name = 线程 B,i = 1

【问题】为什么启动线程不能直接使用run()方法而必须通过start()方法?问的好!线程的运行需要本机操作系统的支持!下面的源码中可以看出,start()实际上是调用了start0()方法,而start0()方法又被native关键字声明,此关键字表示调用本机的操作系统函数,因为多线程的实现需要靠底层操作系统的支持。而run()仅仅是一个类中的一个方法而已。

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    private native void start0();

另外,一个类通过继承Thread类来实现,那么只能调用一次start()方法,如果调用多次,则会抛出“IllegalThreadStateException”异常,因为synchronized的作用。

3.2,实现Runnable接口

通过实现Runnable接口来创建并启动线程的步骤如下:

  • 定义Runnable接口的实现类,并实现该接口的run()方法,该run()方法将作为线程执行体。
  • 创建Runnable实现类的实例,并将其作为Thread的target来创建Thread对象,Thread对象为线 程对象。
  • 调用线程对象的start()方法来启动该线程。
class 类名称 implements Runnable{
    属性...;
    方法...;
    public void run(){
        线程主图;
    }
}
class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }
    public void run() {
        for (int i = 0; i < 2; i++) {
            System.out.println(name + ":" + i);
        }
    }
}

从Thread可以知道,要想启动一个多线程必须要使用start()方法完成,如果继承了Thread类,则可以直接使用start()方法,但是Runnable接口并没有start()方法的定义。此时还是要依靠Thread类完成启动,在Thread类中提供了public Thread(Runnable target)和public Thread(Runnable target,String name)两个构造方法。

class MyThread implements Runnable {
    private String name;
    public MyThread(String name) {this.name = name;}
    public void run() {
        for (int i = 0; i < 2; i++) {System.out.println(name + ":" + i);}
    }
}
public class HelloWord {
    public static void main(String args[]) {
        MyThread thread1 = new MyThread("线程 A");
        Thread t1 = new Thread(thread1);
        t1.start();
        Thread t2 = new Thread(new MyThread("线程 B"));
        t2.start();
    }
}
=========================================================================
线程 A:0
线程 B:0
线程 A:1
线程 B:1

3.3,实现Callable接口

通过实现Callable接口来创建并启动线程的步骤如下:

  • 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有 返回值。然后再创建Callable实现类的实例。
  • 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返 回值。
  • 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
class MyCall implements Callable {
    private String name;

    public MyCall(String name) {
        this.name = name;
    }

    public Object call() throws Exception {
        for (int i = 0; i < 2; i++) {
            System.out.println(name + ":" + i);
        }
        return null;
    }
}
public class Main {
    public static void main(String[] args) {
        MyCall myCall = new MyCall("燕双嘤");
        FutureTask futureTask = new FutureTask(myCall);
        Thread a = new Thread(futureTask);
        a.start();
    }
}

3.4,Thread类&Runnable接口

class Thread implements Runnable {
    private Runnable target;
    public Thread(Runnable target,String name){
        init(null,target,name,0);
    }
    public void init(ThreadGroup g,Runnable target,String name,long stackSize){
        ...
        this.target = target;
        ...
    }
    public void run(){
        if(target!=null){
            target.run();
        }    
    }
}

从源码可以看出来,Thread类也是Runnable接口的子类,但在Thread类中并没有完全地实现Runnable接口中的run()方法。在Thread类中的run()方法调用的是Runnable接口中的run()方法,也就是说此方法是由Runnable子类完成的,所以如果要通过继承Thread类实现多线程,则必须覆写run()方法。

实际上Thread类和Runnable接口之间在使用上还是有区别的,如果一个类继承Thread类,则不适合于多个线程共享资源,而实现Runnable接口,就可以方便地实现资源的共享。

卖票5        卖票5
卖票5        卖票4
卖票4        卖票3
卖票5        卖票2
卖票4        卖票1
卖票3
卖票3
卖票2
卖票1
卖票4
卖票2
卖票3
卖票2
卖票1
卖票1

从上面的结果可以看出来,Runnable接口虽然启动了3个线程,但是3个线程一共才卖5张,即ticket属性被所有线程对象共享。可见Runnable接口相对于Thread类来说有如下优势:

  • 适合多个相同程序代码的线程去处理同一资源的情况。
  • 可以避免由于Java的单继承特性带来的局限。
  • 增强了程序的健壮性,代码能被多个线程共享,代码与数据是独立的。

所以,在开发时建议使用Runnable接口实现多线程。

3.5,Runnable接口&Callable接口

实现Runnable接 口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。因此可以将实现Runnable接口和实现Callable接口归为一种方式。

采用实现Runnable、Callable接口的方式创建多线程的优缺点:

  • 【优势】线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
  • 【优势】在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资 源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  • 【劣势】编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。

采用继承Thread类的方式创建多线程的优缺点:

  • 【劣势】因为线程类已经继承了Thread类,所以不能再继承其他父类。
  • 【优势】编写简单,如果需要访问当前线程,则无须使用Thread.currentThread()方法,直接使用 this即可获得当前线程。

鉴于上面分析,因此一般推荐采用实现Runnable接口、Callable接口的方式来创建多线程。

4,线程的操作

方法名称类型描述
public Thread(Runnable target)构造接收Runnable接口子类对象,实例化Thread对象
public Thread(Runnable target, Strring name)构造接收Runnable接口子类对象,实例化Thread对象,并设置线程名称
public Thread(String name)构造实例化Thread对象,并设置线程名称
public static Thread currentThread()普通返回目前正在执行的线程
public final String getName()普通返回线程的名称
public final int getProperiority()普通返回线程的优先级
public boolean isInterrupted()普通判断目前线程是否被中断,如果是,返回true,否则返回false
public final boolean isAlive()普通判断线程是否在活动,如果是,返回true,否则返回false
public final void join() throws Interrupted普通等待线程死亡

public final synchronized void join(long millis) throws InterruptedException

普通等待millis毫秒后,线程死亡
public void run()普通执行线程
public final void setName(String name)普通设定线程的名称
public final setPririty(int newPriority)普通设定线程的优先级
public static void sleep(long millis) throws InterruptedException普通使目前正在执行的线程休眠millis毫秒
public void start()普通开始执行线程
public String toString普通返回代表线程的字符串
public static void yield()普通将目前正在执行的线程暂停,允许其他线程执行
public final void setDaemon(boolean on)普通将一个线程设置成后台运行

4.1,取得和设置线程的名称

可以通过getName()方法取得线程的名称,还可以通过setName()方法设置线程的名称。线程的名称一般在启动线程前设置,但也允许为已经运行的线程设置名称。允许两个线程对象有相同的名称,但应该尽量避免这种情况的发生。

另外,如果没有设置名称,系统会为其自动分配名称。名字按照Thread-Xx 格式。

在Java中所有的线程都是同时启动的,那个线程先抢到了CPU资源,那个线程就先运行。

class MyThread implements Runnable {
    public void run() {
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class HelloWord {
    public static void main(String[] args) {
        MyThread my = new MyThread();
        new Thread(my).start();
        new Thread(my).start();
        new Thread(my,"燕双嘤").start();
    }
}
=======================================
Thread-0:0
燕双嘤:0
燕双嘤:1
Thread-1:0
Thread-1:1
Thread-0:1

问题:Java程序每次运行至少启动几个线程?

答案:因为Java是多线程编程语言,所以Java程序运行时也是以线程方式运行的,主方法也是一个线程(main),但对于一个Java程序每次运行至少启动两个线程。Java命令执行一个类时,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程,Java本身具备了垃圾回收机制。所以在Java运行时至少会启动两个线程,一个是main线程,另一个是垃圾收集线程。

4.2,判断线程是否启动(isAlive)

class MyThread implements Runnable {
    public void run() {
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class HelloWord {
    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t= new Thread(my,"燕双嘤");
        System.out.println(t.isAlive());
        t.start();
        System.out.println(t.isAlive());
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        System.out.println(t.isAlive());
    }
}
=========================================
false
true
main:0
main:1
true
燕双嘤:0
燕双嘤:1

上面的结果是不确定的,有可能到最后线程已经不存活了,但也有可能继续存活,这要看哪个线程先执行完。主线程有可能比其他线程先执行完,因为线程操作的不确定性,所以主线程有可能最先执行完,那么此时其他线程不会受到任何影响,并不会随着主线程的结束而结束。

4.3,等待(wait)和唤醒(notify)

这两个不能算是线程的独有方法,而是Object的,所有的类都可以使用。

notify()、notifyAll()的区别:

  •  notify() 用于唤醒一个正在等待相应对象锁的线程(随机),使其进入就绪队列,以便在当前线程释放锁后竞争锁, 进而得到CPU的执行。
  • notifyAll() 用于唤醒所有正在等待相应对象锁的线程,使它们进入就绪队列,以便在当前线程释放锁后竞争 锁,进而得到CPU的执行。 

【问题】Java中notify一定会唤醒线程吗?不一定。在Java中,使用wait()方法可以使一个线程进入等待状态,而使用notify()方法可以唤醒一个等待该对象锁的线程。但是,notify()方法并不保证一定会唤醒一个线程。当有多个线程等待同一个对象锁时,notify()方法只会唤醒其中一个线程,但是无法保证唤醒哪个线程。

【问题】如何保证notify一定能唤醒?为了保证可靠的线程唤醒,建议使用notifyAll()方法,它可以唤醒所有等待该对象锁的线程,从而避免竞争问题。

【问题】如何知道notify唤醒那些线程?具体哪个线程被唤醒是不确定的,并且取决于操作系统线程调度器的具体实现。

方法类型描述
public final void wait() throws Interrupted普通线程等待
public final void wait(long timeout) throws Interrupter普通线程等待,并指定等待的最长时间
public final void wait(long timeout,int nanos) throws Interrupter普通线程等待,并指定等待的最长时间,指定最长毫秒以及纳秒
public final void notify()普通唤醒第1个等待线程
public final void notifyAll()普通唤醒全部等待线程

4.4,强制执行(join)和礼让(yield)

    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t= new Thread(my,"燕双嘤");
        t.start();
        for (int i = 0; i < 5; i++) {
            if (i>2){
                try{
                    t.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
===================
main:0
main:1
main:2
燕双嘤:0
燕双嘤:1
main:3
main:4

可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成后才可以继续执行。首先主线程运行,占用了CPU,期间子线程运行处于就绪状态,等待CPU调度,当遇到join()后主线程(main)处于就绪状态,子线程运行,等待子线程结束后主线程继续运行。

class MyThread implements Runnable {
    public void run() {
        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if(i==3){
                Thread.currentThread().yield();
            }
        }
    }
}
public class HelloWord {
    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t1= new Thread(my,"燕双嘤");
        Thread t2= new Thread(my,"杜马");
        t1.start();t2.start();
    }
}
======================================
燕双嘤:0
杜马:0
杜马:1
杜马:2
杜马:3
燕双嘤:1
燕双嘤:2
燕双嘤:3
杜马:4
燕双嘤:4

如果顺序不对或者,其他结果是正常现象,因为CPU,内存都各种影响线程的运行。但是到3后一定会切换线程。

4.5,休眠(sleep)和中断(interrupt)

sleep和wait的区别:

【对象】这两个方法来自不同的类分别是Thread和Object。

【锁机制】最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁)。

【使用范围】wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

【捕获异常】sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。

【唤醒机制】一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;一个对象调用了sleep方法进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态。

调用  Thread.interrupt()  方法会将线程的中断标志设置为 true,如果线程正在阻塞中,则会抛出一个 InterruptedException 异常,如果线程没有阻塞,则需要在代码中检查中断标志来决定如何处理中断。

class MyThread implements Runnable {
    public void run() {
       try{
           Thread.sleep(10000);
       }catch (Exception e){
           e.printStackTrace();
           System.out.println("遇到错误,终止休眠!");
       }
    }
}

public class HelloWord {
    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t= new Thread(my,"燕双嘤");
        t.start();
        try {
            Thread.sleep(2000);
        }catch (Exception e ){
            e.printStackTrace();
        }
        t.interrupt();
    }
}
====================================================
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at Package2.MyThread.run(HelloWord.java:8)
	at java.lang.Thread.run(Thread.java:748)
遇到错误,终止休眠!

一个线程启动之后进入休眠状态,原本是要休眠10s之后再继续执行,但是主方法在线程启动之后的两秒就将其中断,休眠一旦中断之后将执行catch中的代码,并利用里面的return语句返回程序调用处。

4.6,后台线程

Java中,只要前台有一个线程在运行,则整个Java进程都不会消失,所以此时可以设置一个后台线程,这样即使Java进程结束,此后台线程依然会继续执行。要想实现这样的操作,直接使用setDaemon()方法即可。

Thread t= new Thread(my,"燕双嘤");
t.start();
t.setDaemon(true);

4.7,线程的优先级

Java中的线程操作中,所有的线程在运行前都处于就绪状态,那么此时那个线程的优先级高,谁先执行。

定义描述表示的常量
public static final int MIN_PRIORITY最低优先级1
public static final int NORM_PRIORITY中等优先级,是线程的默认状态5
public static final int MAX_PRIORITY最高优先级10
class MyThread implements Runnable {
    public void run() {
        for (int i=0;i<3;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class HelloWord {
    public static void main(String[] args) {
        MyThread my = new MyThread();
        Thread t1= new Thread(my,"燕双嘤");
        Thread t2= new Thread(my,"杜马");
        Thread t3= new Thread(my,"步鹰");
        t1.setPriority(Thread.MAX_PRIORITY);t2.setPriority(Thread.NORM_PRIORITY);t3.setPriority(1);
        t1.start();t2.start();t3.start();
    }
}
====================================================================
燕双嘤:0
燕双嘤:1
燕双嘤:2
杜马:0
杜马:1
杜马:2
步鹰:0
步鹰:1
步鹰:2

线程将根据其优先级的大小来决定哪个线程会先运行,但是并非线程优先级越高就一定会执行,线程的执行是由CPU决定的,受到各种因素影响。主方法(main)的优先级是NORM_PRIORITY,也就是5,可以通过Thread.currentThread().getPriority()验证。

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

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

相关文章

tkinter绘制组件(42)——工具栏按钮组件

tkinter绘制组件&#xff08;42&#xff09;——工具栏按钮组件 引言布局函数结构背景板创建按钮移动背景板完整代码函数 效果测试代码最终效果 github项目pip下载结语 引言 在TinUI中&#xff0c;并不存在工具栏这个概念&#xff0c;但是可以通过使用BasicTinUI控件&#xff…

第二证券:港股交易规则有哪些?

港股的生意规矩&#xff1a; 1、港股生意时间&#xff1a;港股商场的生意时间分为上午和下午两个时段&#xff0c;上午的生意时间通常是9:30至12:00&#xff0c;而下午的生意时间则是13:00至16:00。需求留心的是&#xff0c;港股在周末及法定节假日会休市&#xff0c;此外恶劣…

ECC加密算法:一种高效且安全的加密技术

ECC&#xff08;Elliptic Curve Cryptography&#xff09;&#xff0c;即椭圆曲线密码算法&#xff0c;是一种基于椭圆曲线数学理论的公钥加密算法。自1985年由Neal Koblitz和Victor S. Miller分别独立提出以来&#xff0c;ECC凭借其独特的数学原理和高效的性能&#xff0c;逐渐…

【数据结构】大根堆和小根堆

大根堆实现逻辑 从整棵树的最后一颗子树开始调整&#xff0c;每次都让根节点和左右孩子去比较&#xff0c;如果根节点比左右孩子的最大值要小&#xff0c;那么就将这两个值进行交换&#xff0c;然后此时这颗子树变成了大根堆&#xff0c;再看下一颗树 然后对下一颗树进行相同…

【大模型框架】【基本结构】transformer

论文地址:https://arxiv.org/pdf/1706.03762 1. 整体结构 2. 内部结构 3. 公式 Transformer工作原理四部曲:Embedding(向量化)、Attention(注意力机制)、MLPs(多层感知机)和Unembedding(模型输出)。

域名过期了网站还能访问吗?怎么查询域名过期?

在当今数字化的时代&#xff0c;网站已经成为企业、个人展示形象和提供服务的重要窗口。而域名作为网站的标识&#xff0c;其重要性不言而喻。然而&#xff0c;域名是有一定有效期的&#xff0c;那么当域名过期了&#xff0c;网站还能访问吗&#xff1f;又该如何查询域名的过期…

Mysql的事务隔离级别实现原理

一、事务隔离级别 mysql支持四种事务隔离级别&#xff1a; 读未提交&#xff1a;一个事务可以读取到另一个事务还未提交的数据&#xff1b;读已提交&#xff1a;一个事务可以读取到另一个事务已经提交的数据&#xff1b;可重复读&#xff1a;同一个事务中&#xff0c;无论读取…

【C语言】数组和函数实践:扫雷游戏

扫雷游戏 1. 扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 ⽂件结构设计 2. 扫雷游戏的代码实现&#xff08;1&#xff09;菜单menu函数&#xff08;2&#xff09;设计main函数&#xff08;3&#xff09;设计game函数&#xff08;4…

免费分享:2013中国沙漠(沙地)分布数据集(附下载方法)

沙漠是指地面完全被沙所覆盖、植物非常稀少、雨水稀少、空气干燥的荒芜地区。沙漠亦作“沙幕”&#xff0c;干旱缺水&#xff0c;植物稀少的地区&#xff0c;通常为风成地貌。中国是世界上沙漠面积较大、分布较广、沙漠化危害严重的国家之一&#xff0c;因此我国十分注重防沙治…

发布Meta Segment Anything Model 2 (SAM 2):开启图像和视频分割的新时代

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

DiffusionModel-latent diffusion,VAE,U-Net,Text-encoder

Diffusers StableDdiffusion Latent Diffusion 稳定扩散&#xff08;Stable Diffusion&#xff09;模型中使用的三个关键组件&#xff1a;自编码器&#xff08;VAE&#xff09;、U-Net和文本编码器&#xff08;Text-encoder&#xff09; 由于潜在扩散模型的U-Net操作在低维空…

掌握FluentResults:.NET中优雅处理结果与错误

在.NET开发中&#xff0c;结果和错误处理是构建健壯应用程序的关键部分。FluentResults是一个.NET库&#xff0c;它提供了一种优雅的方式来处理操作结果和错误。它允许开发者以声明性和链式调用的方式构建结果对象&#xff0c;使得代码更加清晰和易于维护。本文将介绍FluentRes…

云原生大数据平台KDP,实战疑难问题解答

智领云的KDP&#xff08;Kubernetes Data Platform&#xff09;是一款自主研发的容器化云原生大数据平台&#xff0c;它是市场上首个能够完全在Kubernetes上部署的大数据平台。KDP深度整合了云原生架构的优势&#xff0c;解决了传统Hadoop大数据平台在架构限制、部署、运维、运…

ctfshow~菜狗杯 flag一分为二

第一步&#xff1a;解压附件得到miku.png文件 第二步&#xff1a;修改图片高度出现一半的flag 第三步&#xff1a;使用盲水印提取工具得到另一半工具 ctfshow{FirstPRTSecondPrTMikumiku~}

认识systemctl

1: 概述 systemctl命令可以帮助systemd管理和维护所有服务,系统启动完成后,systemctl命令会向systemd发送消息进行服务启动和停止等操作,想知道为什么systemctl命令可以向systemd传递消息吗,其实它是通过一个叫D_Bus(desktop bus)桌面总线的东西实现这一点,它可以并行的处理多…

劝你先别更新!!最新Stable Diffusion WebUI 1.10已来!WebUI终于支持SD3大模型了!你跑起来了么?

你的SD3大模型在SD WebUI1.10.0中跑起来了么&#xff1f; 今天发现Stable Diffusion WebUI于昨日推出了最新SD WebUI1.10.0 版本。令人比较兴奋的是该版本支持了SD3大模型&#xff0c;同时也新增了DDIM CFG采样器。主要更新内容如下&#xff1a; 最新版本地址&#xff1a; 更新…

跨境电商TikTok达人合作策略:驱动口碑传播,助力经济增长

通过与TikTok达人的深度合作&#xff0c;跨境电商不仅能够有效提升品牌曝光度和销售额&#xff0c;还能激发用户的口碑传播&#xff0c;形成强大的“口碑经济”效应。本文Nox聚星将和大家分析这种合作模式如何引发口碑经济效应&#xff0c;并探讨如何通过优化合作策略&#xff…

Python酷库之旅-第三方库Pandas(058)

目录 一、用法精讲 221、pandas.Series.interpolate方法 221-1、语法 221-2、参数 221-3、功能 221-4、返回值 221-5、说明 221-6、用法 221-6-1、数据准备 221-6-2、代码示例 221-6-3、结果输出 222、pandas.Series.isna方法 222-1、语法 222-2、参数 222-3、功…

JAVA的String类的contains方法,Indexof方法不使用KMP算法

今天做力扣的一道题目&#xff0c;在一个字符串中找出字符串的时候&#xff0c;想到了JAVA有一个contains方法&#xff0c;于是我去看了一下源码&#xff0c;发现他就是简单地调用了一下java的indexof方法&#xff0c; 然后我又去看了Indexof的源码&#xff0c;发现他就是简单的…