多线程—— Thread 类及常见用法(详解)

news2024/10/10 15:05:08

·前言

        本篇文章会介绍 Java 中的 Thread 类常见的构造方法、常见属性及其常见的用法,会列举一些代码例子来更直观的感受每个方法是作用,文章还会介绍 Java 中线程的状态及他们之间的转换关系,那么下面就开始今天的分享吧~~

一、常见构造方法

        Thread 类的常见构造方法如下表所示:

Thread 类常见构造方法
方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并对线程命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并对线程命名

1. Thread()

        下面对 Thread() 这个构造方法进行演示,代码与运行结果如下:

public class ThreadDemo2 {
    public static void main(String[] args) {
        // 实例化一个线程对象
        Thread t = new Thread();
        // 真正的去申请系统线程,参与 CPU 调度
        t.start();
    }
}

        通过运行结果可以看出没有任何的响应,原因在通过对 Thread 类的源码进行观察找到了,如下图所示(主要源码部分摘取):

        Thread类实例化的对象,在调用 start 方法后会自动调用 run 方法,然而这里 run 方法什么都不执行,所以利用上面代码的运行结果没有任何响应。

2.Thread(Runnable target)

        Thread(Runnable target) 这个构造方法在前面创建线程时使用过,具体代码及运行结果如下所示:

// 创建一个 Runnable 的实现类,并实现 run 方法
// Runnable 主要描述的是线程的任务
class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("hello thread!!!");
    }
}

public class ThreadDemo4 {
    public static void main(String[] args) {
        // 实例化 Runnable 对象
        Runnable runnable = new MyThread2();
        // 实例化线程对象,并绑定任务
        Thread t = new Thread(runnable);
        // 真正的去申请系统线程,参与 CPU 调度
        t.start();
    }
}

3.Thread(String name)

        每个线程创建后都有名字,通过 jconsole 可以看见每个线程的名字,为了区分每个线程,在创建线程的时候可以使用 Thread(String name) 构造方法传一个字符串作为线程的名字,由于直接使用 Thread(String name) 这个构造方法,其中没有重写 run 方法所以导致线程启动后没有进行任何操作就被销毁了,这里就无法看见线程的名字了。

4.Thread(Runnable target, String name)

        这里的 Thread(Runnable target, String name) 构造方法是与 Thread(Runnable target) 构造方法很相似的,就是这个构造方法在创建线程时手动传了一个字符串作为线程的名字,这个我们可以通过创建一个 Runnable 的实现类,实现一个 run 方法,在 run 方法中设置一个死循环,不让这个线程结束,来观察这个构造方法的效果,代码及运行结果如下所示:

// 创建一个 Runnable 的实现类,并实现 run 方法
// Runnable 主要描述的是线程的任务
class MyThread2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo4 {
    public static void main(String[] args) {
        // 实例化 Runnable 对象
        Runnable runnable = new MyThread2();
        // 实例化线程对象,并绑定任务
        Thread t = new Thread(runnable,"这是我的线程1");
        // 真正的去申请系统线程,参与 CPU 调度
        t.start();
    }
}

        下面通过 jconsole 来查看一下该线程的名字,如下图所示:

        这里对于给线程起名和线程名称放在下面常见属性中进行介绍。

二、常见属性

        Thread 类的常见属性如下表所示:

Thread 类常见属性
属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

1.ID

        ID 是线程的唯一标识,不同的线程不会重复。

2.名称

        线程的名称主要是各种调试工具会用到,在我们上面常见构造方法中也提到了有关线程的起名与线程名称的一点介绍,这里再进行更细致的介绍,大致有以下几点:

  1. 如果我们不手动给线程起名,默认的线程名是按照 Thread-0、Thread-1、Thread-2……这种规律来进行命名;
  2. 给不同的线程起不同的名字,这对于线程的执行是没有什么影响的,起名主要是为了在代码出问题时方便调试; 
  3. 线程之间的名字可以重复,一般在同一个工作中,需要用到多个线程时,就可以把这几个线程都起一样的名字;
  4. 在对线程起名字时也不要乱起,最好有一定的描述性。

3.状态

        在学习进程时,我们了解到进程有状态(就绪状态、阻塞状态和运行状态),在线程中也有状态, Java 中对线程的状态又进一步的区分(比系统原生的状态要更丰富一些),关于线程状态的内容在文章后面还会详细的介绍。

4.优先级

        优先级高的理论上来说更容易被调度到,但是在 Java 中,设置优先级效果不是很明显,这是由于系统的随机调度。

5.是否后台线程

        和后台线程相对的还有前台线程,前台线程的运行会阻止进程结束,后台线程的运行不会阻止进程结束,所以关于是否后台线程要记住一点:JVM会在一个进程的所有前台线程结束后才会结束运行。下图是上面演示 Thread(Runnable target, String name) 构造方法的使用中用 jconsole 获取的线程信息,根据下图可以发现里面没有 main 线程,这是因为 main 线程已经执行结束了,但是该进程没有结束,是因为还有我们创建的前台线程没有结束,所以进程不会结束。

         我们可以使用 setDaemon() 方法来设置我们创建的线程为后台线程(不设置默认是 false 代表创建的线程默认是前台线程),这样不管我们创建的线程有没有执行完,只要 main 线程执行完毕就会结束,对此我们对上面代码进行一些修改,修改代码及运行结果如下所示:

// 创建一个 Runnable 的实现类,并实现 run 方法
// Runnable 主要描述的是线程的任务
class MyThread2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo4 {
    public static void main(String[] args) {
        // 实例化 Runnable 对象
        Runnable runnable = new MyThread2();
        // 实例化线程对象,并绑定任务
        Thread t = new Thread(runnable,"这是我的线程1");
        // 设置 t 为后台线程
        t.setDaemon(true);
        // 真正的去申请系统线程,参与 CPU 调度
        t.start();

        // 休眠一秒,让创建的线程先执行一秒
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

         这里我们创建的线程中写了一个死循环,然而在 main 线程执行结束后也一起结束了,这也进一步验证了一个进程的所有前台线程结束后就会会结束运行,在用 jconsole 获取线程中我们也看见了很多其他线程,这些线程也都是后台线程,后台线程不会阻止进程结束,前台线程会阻止进程结束。

6.是否存活

        这里是否存活可以简单理解为 run 方法是否运行结束了,具体演示代码及运行结果如下:

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello thread!!!");
            // 休眠一秒,用来获取线程的存活的反馈
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println("线程 t 未启动:->" + t.isAlive());
        t.start();
        System.out.println("线程 t 未执行完:->" + t.isAlive());
        // 休眠两秒秒,确保线程 t 的 run 方法执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程 t 执行完毕:->" + t.isAlive());
    }
}

         isAlive() 方法显示的线程存活信息表示了,内核中的线程(TCB)是否还存在,在我们的 Java 代码中定义的线程对象(Thread)实例,虽然表示一个线程,但是这个对象的生命周期和我们内核中的 TCB 的生命周期是完全不一样的,在我们使用 new + 构造方法时创建线程对象 t 时,此时 t 对象有了,但是内核 TCB 还没有,所以调用 isAlive() 方法得到的就是 false ,执行完 t.start() 时,才真正的在内核中创建出这个 TCB ,此时调用 isAlive() 方法得到的就是 true ,当线程 t 的 run 方法执行完,此时内核中的线程也就结束了(内核中的TCB就释放了),但是 t 变量可能还存在,不过因为 TCB 已经释放所以调用 isAlive() 方法得到的就是 false。

7.是否被中断

        关于线程是否被中断在文章后面会进一步说明。

三、启动线程

1. start() 方法

        Thread 类使用 start() 方法来启动一个线程,对于同一个 Thread 对象来说,start() 方法只能调用一次,对同一个 Thread 对象调用多次 start() 方法,代码及运行结果如下所示:

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello thread");
        });
        t.start();
        t.start();
    }
}

        所以想要启动更多的线程就要创建新的的线程对象,调用 start() 方法会创建出新的线程,本质上是 start() 方法会调用系统的 API 来完成创建线程的操作。

2. start() 方法与 run() 方法的区别

        这个问题的引入应该与下面这两段代码的执行有一定的关系。

        调用 start() 方法,代码与运行结果如下:

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello thread");
        });
        t.start();
    }
}

         调用 run() 方法,代码与运行结果如下:

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello thread");
        });
        t.run();
    }
}

 

        通过上面两段代码及各自的运行结果可以看出,不管是调用 start() 方法还是调用 run() 方法,得到的运行结果都是一样的,此时就会引入 start() 方法和 run() 方法有什么区别呢?这样的问题,我再通过下面的两段代码进一步区分一下这两个方法。

        调用 start() 方法,代码与运行结果如下:

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (true) {
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        while (true) {
            System.out.println("hello main!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

        调用 run() 方法,代码与运行结果如下:

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (true) {
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.run();
        while (true) {
            System.out.println("hello main!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

         再通过上面这两段代码及他们的运行结果可以看出,调用 start() 方法是两个线程的死循环在一起并发的执行,而调用 run() 方法时,只会执行 run() 方法中的死循环,由此可以看出他们两个是有区别的,他们的区别如下:

        调用 start() 方法是创建了一个新的线程,并调用其中的 run() 方法,此时执行 run() 中代码与执行 main 中代码的是两个线程,而直接调用 run() 方法,只是是在 main 线程中调用,这里不涉及到创建新的线程,这也是他们本质的区别。

四、中断线程

        当一个线程正在运行时,我们可不可以提前将这个正在运行的线程结束呢?这就是线程中断,这里我来介绍两种中断线程的方法。

1.引入标志位

        这里是通过共享标记来进行两个线程的沟通,来达到使线程正常退出,也就是执行完 run() 方法后线程终止,演示代码与运行结果如下:

public class ThreadDemo9 {
    private static boolean isQuit = false;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (!isQuit) {
                System.out.println("hello thread!!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("线程 t 工作完毕");
        });
        t.start();
        // 让 t 线程执行两秒后结束
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        isQuit = true;
        System.out.println("让 t 线程结束");
    }
}

         通过 main 线程修改 isQuit 标志位的值为 true 来中断线程 t 的运行。这里 main 线程想要 t 线程结束,大前提一定是 t 线程的代码对这样的逻辑有所支持,而不是说 t 线程这里的代码随便怎么写都可以提前结束,如果 t 线程中的代码没有配合, main 线程是无法让 t 提前结束的。

2.调用 interrupt() 方法

        上面中断线程的写法并不是很优雅,所以 Thread 类还提供了一种更优雅的选择,那就是调用 interrupt() 方法,代码及运行结果如下:

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            // isInterrupted() 方法相当于标志位 isQuit
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread!!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程 t 工作完毕");
        });
        t.start();
        // 让 t 线程执行三秒后结束
        Thread.sleep(3000);
        // 相当于设置 isQuit 为 true
        t.interrupt();
        System.out.println("让 t 线程结束");
    }
}

         上述代码中 isInterrupted() 方法相当于用来判定标志位,interrupt() 方法相当于设置标志位,通过运行结果可以看出,这里并没有按照预想的那样中断线程,主要原因是 sleep 引起的,在上面代码执行 sleep 的过程中,调用了 Interrupt 方法,导致 sleep 休眠时间还没有到,就被提前唤醒了,sleep 被唤醒会做以下两件事:

  1. 抛出 InterruptedException 异常,被 catch 获取到;
  2. 清除 Thread 对象的 isInterrupt 标志位。

        虽然我们在代码中已经通过 interrupt 方法把标志位设为 true 了,但是 sleep 提前唤醒操作又把标志位重新设回 false 了,此时循环就继续执行了。

        想要解决上面的问题很简单,在捕获 sleep 异常的 catch 语句中加上 break 就可以了,此时代码及运行结果如下所示: 

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            // isInterrupted() 方法相当于标志位 isQuit
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread!!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
            System.out.println("线程 t 工作完毕");
        });
        t.start();
        // 让 t 线程执行三秒后结束
        Thread.sleep(3000);
        // 相当于设置 isQuit 为 true
        t.interrupt();
        System.out.println("让 t 线程结束");
    }
}

        上图运行结果中的异常信息是由 e.printStackTrace(); 这条语句打印的,如果感觉看着别扭可以将这条语句删除,就不会出现异常的信息了。

五、等待线程

        在我们多线程代码中,由于系统的随机调度,抢占式执行,每个线程的执行顺序是不能确定的,虽然线程的调度是无序的,但是我们有的时候又希望能够控制线程之间的结束的先后顺序,此时调用 join 方法就是一种控制方式,join 这个方法会让等待线程阻塞,一直阻塞等到被等待线程执行完 run 方法,比如, t2 线程等待 t1 线程,此时,就一定是 t1 先结束 t2 后结束, join 方法就可能使 t2 线程阻塞。利用下面代码来对 join 方法做进一步的介绍与演示,代码及运行结果如下:

package thread_test;

public class ThreadDemo10 {
    public static long num1 = 0;
    public static long num2 = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5_000_00; i++) {
                num1 += i;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 5_000_00; i <= 1_000_000; i++) {
                num2 += i;
            }
        });
        t1.start();
        t2.start();
        // 等待 t1 执行结束
        t1.join();
        // 等待 t2 执行结束
        t2.join();
        long sum = num1 + num2;
        System.out.println("num1 + num2 = " + sum);
    }
}

 

        上述代码是利用两个线程来求从 0 加到 1000000 的值,这里就需要我们 main 线程等待 t1 线程与 t2 线程都执行完再讲两个线程算出来的结果进行汇总得出最终的结果。

        上面的介绍让大家理解等待线程是为了什么,这里我再对 join 方法进一步介绍:

join 方法
方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但是等待时间的精度更高

        在前面代码中,我们调用的都是没有参数的 join 方法,这也可以叫做“死等”,这种做法不是很科学,如果代码因为死等导致程序卡住,将无法处理后续逻辑,就会出现很严重的问题,所以在上面表格中还列举了有关 join 方法的两种形式,这两种是带有等待时间的等,等时间达到了超时时间就不再继续等待了,同时 interrupt 方法也可以把阻塞等待的 join 方法提前唤醒。 

        这里需要明确,哪个线程调用 join 方法,哪个线程阻塞,此时阻塞就明确了两个线程结束的先后顺序。

六、获取当前线程引用

        获取当前线程引用的方法如下所示:

currentThread() 方法
方法说明
public static Thread currentThread();返回当前线程对象的引用

        这个方法在前面代码中也有所应用,再以下列代码及运行结果做进一步演示,如下所示:

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

 

        Thread.currentThread() 获取到的是当前线程的引用(Thread 的引用),上面代码是在 main 线程中调用的这个方法,所以获取到的是 main 线程的引用,所以打印 main 线程的名字运行结果显示的就是 main了。 

        如果当前线程继承 Thread ,那么直接使用 this 就可以拿到线程的实例;如果当前线程是实现 Runnable 接口或者是使用 lambda 的方式来创建的,就不能使用 this 了,此时 this 已经不再指向 Thread 对象了,这时就只能调用 Thread.currentThread() 方法来获取当前线程的引用了。

七、休眠当前线程

        休眠当前线程用到的是 sleep 方法,在上面代码中已经多次用到这个方法,这里再针对 sleep 提前唤醒清空标志位这点进行进一步的介绍:

        sleep 清空标志位这一点给我们留了很大的操作空间, 比如我们在代码中需要一个线程休眠一秒,结果一秒还没有到,就要终止这个线程,这两个操作相当于是自相矛盾的,此时,我们希望写更多的代码来对这样的情况进行具体的处理,这些代码就是写在 catch 语句中的,大致分为以下这三种情况:

  1. 让线程立即结束(相当于在 catch 语句中直接加上 break);
  2. 让线程不结束,继续运行 (不加 break);
  3. 让线程执行一些其他逻辑再结束(在 catch 语句中,写一些其他代码之后再 break)。

八、线程状态

        线程的状态是一个枚举类型 Thread.State 通过下面代码就可以将线程的状态枚举出来,代码及运行结果如下所示:

public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}

 

        如上图运行结果所示列举出了线程的状态,下面我再对每个状态进行一个介绍:

  1.  NEW:Thread 对象已经创建好了,但是还没有调用 start 方法在系统中创建线程;
  2. RUNNABLE:表示这个线程正在 CPU 上执行,或者准备就绪随时可以去 CPU 上执行;
  3. BLOCKED:由于锁竞争,引起了阻塞;
  4. WAITING:不带时间的阻塞(死等),必须满足一定的条件才会解除阻塞,调用 join 方法或者 wait 方法都会进入 WAITING;
  5. TIMED_WAITING:指定时间的阻塞,就在到达一定时间时间之后自动解除阻塞,调用 sleep 方法或者使用带有超时时间的 join 方法都会进入TIMED_WAITING;
  6. TERMINATED:Thread 对象仍然存在,但是系统内部的线程已经执行完毕了。

        介绍完每个状态的意思,下面我画一个粗略的图来描述一下各个状态之间的转换关系,大致如下图所示:         了解这些状态的最大作用就是在我们写多线程代码出现 bug 的时候,可以作为一个重要的参考依据,比如我们写的代码运行时卡住了,就意味着一些关键的线程阻塞了,这时我们可以用 jconsole 来观察线程的状态,可以分析出一些原因,还有在前面提到一个 Thread 对象只能 start 一次,这个也是和线程状态密切相关的,只有处于 NEW 状态的线程才能调用 start 方法。

·结尾

        文章到此就要结束了,本篇文章内容偏多,但是都是一些基础的内容,不过这些内容也十分重要,这是我们在用 Java 进行多线程编程的基石,如果本篇文章对您有所帮助,希望能给我个三连支持一下吧~~您的支持就是我最大的动力,当然,如果您本篇文章的某个知识点还有疑问,可以在评论区,或者私信中与我讨论,我们下一篇文章再见吧┏(^0^)┛!!!

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

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

相关文章

ant-design-vue 可输入表格的校验方法

1、思路&#xff0c;首先用a-form包裹a-table&#xff0c;( 主要是name的取值问题&#xff0c;要严格按照[数据源,index,校验的字段]来) <a-form ref"form" :model"formData" :rules"rules"><a-table :dataSource"formData.table…

charAt,chartCodeAt,codePointAt,fromCodePoint,fromCharCode

生僻字的length算2,有些空格是特殊空格,比如\u3000 u3000不是全角空格&#xff0c;u3000是表意字空格&#xff08;Ideographic Space&#xff09;&#xff0c;宽度和一个表意字&#xff08;汉字&#xff09;相同。它应当被当做汉字来处理。比如&#xff0c;在一些排版中&#x…

Vxe UI vue vxe-table select 下拉框选项列表数据量超大过大时卡顿解决方法

Vxe UI vue vxe-table vxe-grid select 下拉框选项列表数据量超大过大时卡顿解决方法 查看 github vxe-table 官网 vxe-table 本身支持虚拟滚动&#xff0c;数据量大也是支持的&#xff0c;但是如果在可编辑表格中使用下拉框&#xff0c;下拉框的数据量超大时&#xff0c;可能…

【源码+文档+调试讲解】宜家宜业物业管理系统node.js框架

摘 要 近年来&#xff0c;科技飞速发展&#xff0c;在经济全球化的背景之下&#xff0c;互联网技术将进一步提高社会综合发展的效率和速度&#xff0c;互联网技术也会涉及到各个领域&#xff0c;而宜家宜业物业管理系统在网络背景下有着无法忽视的作用。信息管理系统的开发是…

Chromium 如何构建一个单独exe c++

1、在src目录下新建一个jdtest文件夹 src\jdtest 2、在jdtest文件下添加BUILD.gn jdtest.cc build.gn 内容如下&#xff1a; # Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file…

python27_strip()去除函数

strip()去除函数 # 示例字符串 s1 "*hello*world*oh*yeah*" s2 " helloworldohyeah "# 使用 strip() 去除两端的 * def StrStrip(a):result_strip a.strip("*")return result_strip# 替换成空字符串 def StrReplaceNull(a):result_empty a.…

OOOPS:零样本实现360度开放全景分割,已开源 | ECCV‘24

全景图像捕捉360的视场&#xff08;FoV&#xff09;&#xff0c;包含了对场景理解至关重要的全向空间信息。然而&#xff0c;获取足够的训练用密集标注全景图不仅成本高昂&#xff0c;而且在封闭词汇设置下训练模型时也受到应用限制。为了解决这个问题&#xff0c;论文定义了一…

软考《信息系统运行管理员》- 4.2信息系统软件运维的管理

4.2信息系统软件运维的管理 管理流程 信息系统软件运维服务的四个关键要素是&#xff1a;人员、资源、技术和过程&#xff0c;每个要素通过关键 指标反映运维服务的能力。 人员 确保提供信息系统软件运维服务的相关人员具备应有的运维服务能力&#xff0c;主要从人员管理、 …

掌握未来:2025年秋招LLM及多模态模型面试精华

目录 大模型常用微调方法LoRA和Ptuning的原理介绍Stable Diffusion的原理为何现在的大模型大部分是Decoder-only结构如何缓解LLMs复读机问题为什么Transformer块使用LayerNorm而不是BatchNormTransformer为何使用多头注意力机制监督微调SFT后LLM表现下降的原因微调阶段样本量规…

【大数据】Spark弹性分布式数据集RDD详细说明

文章目录 整体介绍一、定义与特性二、操作与转换三、存储级别与持久化四、依赖关系与容错机制五、优化与性能调优 常见操作支持的数据格式1.文本文件 (Text Files)2. CSV 文件3. JSON 文件4. Parquet 文件5. Sequence Files6.Hadoop文件读取A. 读取HDFS上的文本文件B. 使用Hado…

深度学习基础—人脸识别

在人脸识别领域&#xff0c;常常有两个词容易被混淆&#xff0c;人脸验证和人脸识别。人脸验证是输入一个人的照片和名字&#xff08;或者ID&#xff09;&#xff0c;验证这个人是否和名字相符。人脸识别是输入一个人的照片&#xff0c;识别这个人是否是数据库中存在的人。人脸…

VScode中配置可编写C/C++代码

VScode的下载 下载地址&#xff1a;https://code.visualstudio.com/ 安装中文插件 重启后&#xff0c;生效&#xff01;&#xff01;&#xff01; 下载和配置MinGW-w64 https://github.com/niXman/mingw-builds-binaries/releases 这里可以自行选择线程模型WIN32或posix&a…

【Linux报错】“-bash: cd: too many arguments“

问题描述 今天使用 cd 想要调整某个文件目录时&#xff0c;发现以下报错 原因分析&#xff1a; arguments 是参数的意思&#xff0c;该报错提示参数过多&#xff0c;意味着系统识别到了多余参数 本质原因&#xff1a;你的命令中输入了多余的 ”空格“ &#xff0c;检查一…

手写Spring第三篇番外,反射的基本使用

上一篇发出去之后&#xff0c;我有一个朋友说 beanDefinition.getBeanClass().newInstance() 这句代码太突兀了&#xff0c;就像是在甜甜的睡梦中&#xff0c;突然脚踩悬崖惊醒。 像我这种插朋友两刀的人必须安排了&#xff0c;不止安排 newInstance 还把反射基本用法也给安排了…

耳夹式耳机哪个品牌音质好?耳夹式蓝牙耳机排行榜!

当下&#xff0c;耳夹式蓝牙耳机愈发受到大众的青睐。不管是在上下班的途中、进行运动锻炼之时&#xff0c;还是休闲居家的日子里&#xff0c;这种耳机都能为使用者带来极为便捷的体验。不过&#xff0c;市面上的耳夹式蓝牙耳机品牌繁杂多样&#xff0c;产品品质也是良莠不齐&a…

【win10】VMware Workstation 16安装win10专业版及安装VMware Tools操作说明

参考链接 VMware虚拟机安装win10系统教程&#xff08;巨细&#xff09;_vmware安装win10-CSDN博客https://blog.csdn.net/gdidea/article/details/129523700 win10专业版安装说明 下载win10安装包 百度网盘 链接: https://pan.baidu.com/s/1kf4ORdXYgcqwAz2j86LSZw?pwdk4…

MySQL(八)——索引

文章目录 索引索引的数据结构索引的具体结构页的概念独立表空间文件页的结构页文件头和页文件尾页主体页目录数据页头 B树在索引中的应用索引分类按数据结构分按字段特性分按物理存储分按字段个数分 索引语法创建索引查看索引删除索引 索引优化SQL执行频率慢查询日志profile详情…

【机器学习导引】ch3-线性模型-2

优化理论&#xff1a;梯度下降&#xff08;Gradient Descent&#xff09; 梯度下降法的基本思路 梯度下降法是一种优化算法&#xff0c;目的是找到函数 f ( x ) f(x) f(x) 的最小值。图中提到“如果能找到一个序列 x 0 , x 1 , x 2 , … x_0, x_1, x_2, \dots x0​,x1​,x2​…

有手就会!低代码让你告别繁琐开发流程

近几年&#xff0c;随着低代码与无代码相关话题的火热&#xff0c;逻辑编排作为其重要构成部分也备受关注&#xff0c;集团内外不乏优秀的实践。之前在做技术调研时发现了不少业内逻辑编排相关的方案&#xff0c;陆续整理记录下来。今天先为大家带来低代码开发领域的 JNPF。 1.…

如何有效恢复受污染除砷树脂的功能?

在半导体制造领域&#xff0c;砷是一种常见的杂质元素。然而&#xff0c;砷的存在不仅会严重影响芯片的性能和可靠性&#xff0c;还会对生产环境和人体健康构成威胁。通过使用高效的除砷树脂&#xff0c;可以有效地去除水中的砷元素。 然而&#xff0c;一旦除砷树脂受到污染&am…