java线程

news2025/4/3 19:36:31

1.创建线程和运行线程

1.1.方式一: 直接使用Thread线程对象创建线程

@Slf4j
public class TestThread {

    public static void main(String[] args) {
        //创建一个线程,并且指定线程名称为"t1"
        Thread thread = new Thread("t1") {
            @Override
            public void run() {
                //子线程中要执行的任务
                log.info("子线程中要执行的任务");
            }
        };

        //启动线程
        thread.start();

        //主线程要执行的任务
        log.info("main线程要执行的任务");
    }
}

在这里插入图片描述
可以看到,两个线程是同时执行的

1.2.方式二: 使用Runnable线程任务对象配合Thread线程对象创建线程

把线程和任务(即要执行的代码)分开,其中Thread代表线程,Runnable表示可运行的任务(线程要执行的代码);

@Slf4j
public class TestThread2 {
    public static void main(String[] args) {
        //创建子线程要执行的任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.info("子线程要执行的任务");
            }
        };

        //创建线程对象,执行子线程中要执行的任务
        Thread thread = new Thread(runnable,"t2");

        //启动子线程
        thread.start();

        //主线程要执行的任务
        log.info("主线程要执行的任务");
    }
}

在这里插入图片描述
可以看到,两个线程也是同时执行的!

java8以后可以使用Lambda表达式来精简代码

@Slf4j
public class TestThread2 {
    public static void main(String[] args) {
        //创建线程要执行的任务对象
        Runnable task1 = (()->{
            log.info("子线程要执行的任务");
        });

        //创建线程对象
        Thread t1 = new Thread(task1, "t1");
        t1.start();

        //主线程中要执行的任务
        log.info("主线程要执行的任务");
    }
}

两种方式对比:

①.方式一是把线程和任务合并在了一起;方式二是把线程和任务分开了;
②.用Runnable更容易与线程池等高级API配合;
③.用Runnable让任务类脱离了Thread继承体系,更灵活;

1.3.方式三: 使用FutureTask任务对象配合Thread线程对象创建线程

FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况;

@Slf4j
public class TestThread3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建任务对象,注意:泛型表示任务返回结果的类型
        //构造函数的参数列表是一个callable接口,该接口里面的call()方法可以抛出异常,而runnable接口中的run()方法无法抛出异常;
        FutureTask<Integer> task2 = new FutureTask<Integer>(() -> {
            log.info("子线程要执行的任务");
            return 1;
        });

        //创建线程对象
        Thread t2 = new Thread(task2, "t2");
        t2.start();

        //主线程阻塞,同步等待task任务执行完毕的结果
        Integer result = task2.get();
        log.info("子线程任务的执行结果是:{}", result);
    }
}

在这里插入图片描述

2.查看进程线程的方法

2.1.windows平台

1>.任务管理器可以查看进程和线程数,也可以用来杀死进程;

2>.查看进程

tasklist

3>.进程过滤

tasklist | findstr <关键字>

4.kill某个进程

taskkill <PID>

或者

taskkill /F /<PID>

2.2.Linux平台

1>. 查看所有的进程

# ps -ef 

2>.进程过滤

# ps -ef | grep <关键字>

3>.查看某个进程(PID)的所有线程

# ps -fT -p <PID>

4>.kill某个进程

# kill <PID>

5>.动态显示进程信息,按大写H切换是否显示线程

# top 

6>.查看某个进程(PID)的所有线程

# top -H -p <PID> 

2.3.java环境中查看进程

1>.命令查看所有java进程

jps

2>.查看某个正在运行的Java进程(PID)的所有线程状态(命令行界面)

jstack <PID>

3>.查看某个Java进程(PID)中线程的运行情况(图形界面)

jconsole <PID>

2.4.使用jconsole工具进行远程进程监控配置

1>.运行Java类:

# java -Djava.rmi.server.hostname='远程通信IP地址' -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port='远程通信端口' -Dcom.sun.management.jmxremote.ssl=是否安全连接 -Dcom.sun.management.jmxremote.authenticate=是否认证java类

2>.修改"/etc/hosts"文件将"127.0.0.1"映射至主机名;

3>.在jconsole连接页面选择远程连接,然后配置远程进程通信IP地址和端口

IP地址:端口

注意: 如果要认证访问,还需要做如下步骤:

–1).复制jmxremote.password文件;
–2).修改jmxremote.password和jmxremote.access文件的权限为600,即文件所有者可读写;
–3).连接时填入controlRole(用户名),R&D(密码);

3.线程运行原理

3.1.栈与栈帧

Java中的栈stack其实就是Java Virtual Machine Stacks (Java虚拟机栈)!

1>.我们都知道JVM中有堆、栈、方法区等内存区域,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存;

①.每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存;

  • 线程每调用一次方法(之前)就会为这个方法产生一块栈帧内存,当方法执行完毕,方法对应的栈帧内存就会被释放/回收;

②.每个线程(同一个线程内)只能有一个活动栈帧(Frame),对应着当前正在执行的那个方法;

  • 线程每次只能执行/调用一个方法;

2>.案例
①.代码

public class TestFrames {
    public static void main(String[] args) {
        //调用方法method1
        //线程在进行方法调用的时候会通过栈帧中的"返回地址"记录将要被调用的这个方法的起始/入口位置,等到被调用的方法执行完毕,然后根据"返回地址"回到方法调用的起始/入口位置,之后继续执行下一行代码;
        method1(1);   //这一行代码添加一个断点
    }

    public static void method1(int x) {
        int y = x + 1;

        //调用方法method2
        Object o = method2();
        System.out.println(o);
    }

    public static Object method2() {
        Object o = new Object();
        return o;
    }
}

②.Debug调试程序
在这里插入图片描述
可以看到栈是一种先进后出的数据结构!

3>.栈帧图解
在这里插入图片描述
在这里插入图片描述

3.2.多线程

1>.代码如下:

public class TestFrames2 {
    public static void main(String[] args) {
        //创建一个子线程
        new Thread("t1") {
            @Override
            public void run() {
                method1(10);  //在这里添加断点,并且右键断点将类型改成Thread
            }
        }.start();

        //主线程调用method1
        method1(10);  //这里也添加一个断点,并且右键断点将类型改成Thread

    }

    public static void method1(int x) {
        int y = x + 1;

        Object o = method2();
        System.out.println(o);
    }

    public static Object method2() {
        Object o = new Object();
        return o;
    }
}

在这里插入图片描述
2>.Debug调试程序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分别调试两个线程,将某个线程的代码执行完毕,观察两个线程生成的栈帧.可以发现,某一个线程里面的代码都执行完毕之后,它生成的所有栈帧全部释放了,而另外一个代码还没有执行完毕的线程生成的栈帧依然存在,也就是说线程生成的栈帧是相互独立的!!!

3.3.线程的上下文切换(Thread Context Switch)

每个线程只能分配到CPU短时间内的执行权,CPU时间片用完之后,那么CPU的执行权就要交出来,这个线程就处于挂起状态(Runnable),等待再次分配CPU时间片,线程从使用CPU到不使用CPU,这就是一次线程的上下文切换;

1>.因为以下一些原因导致CPU不再执行当前的线程,转而执行另一个线程的代码;

①.线程的CPU时间片用完;

②.垃圾回收;

  • 垃圾回收会暂停所有的工作线程,开启垃圾回收线程进行垃圾回收;

③.有更高优先级的线程需要运行;

  • 如果发现有更高优先级的线程需要运行,那么当前线程就要交出CPU执行权,让优先级更高的线程先执行;

④.线程自己调用了sleep、yield、wait、join、park、synchronized、lock等方法;

2>.当线程上下文切换发生时,需要由操作系统保存当前线程的状态(类似于断点续传),并恢复另一个线程的状态,Java中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条jvm指令的执行地址,程序计数器是线程私有的;

①.状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等;

②. 频繁Context Switch会影响性能;

4.线程中常见的方法

4.1.概述

在这里插入图片描述
在这里插入图片描述

4.2.start与run方法

1>.代码如下:

@Slf4j
public class TestRun {
    public static void main(String[] args) {
        //创建一个子线程
        Thread thread = new Thread("t1") {
            @Override
            public void run() {
                log.info("running...");
            }
        };

        //直接执行线程的run方法,run方法是在main主线程中执行!
        //子线程中设置的阻塞代码仍然会被执行,或者说子线程中的阻塞代码仍然会起作用的!
        //thread.run();  // [main] [INFO ] 16 com.xp.thread.TestRun --- running...
        //log.info("do other things...");  //先执行上面的run方法,然后再执行当前代码;

        //调用start方法启动一个新的线程,由新的线程去执行run方法
        thread.start();  //[t1] [INFO ] 21 com.xp.thread.TestRun --- running...
        log.info("do other things...");  //先执行当前代码,然后再执行子线程的run方法
    }
}

2>.说明

①.当在main线程中直接调用/执行子线程中的run(),子线程的run()仍在main线程中运行,run()里面的代码执行还是同步的;

②.在main线程中通过线程的start()启动一个新的线程,run()在子线程中运行, run()里面的代码执行是异步的;

3>.小结

①.直接调用run()是在主线程中执行了run,没有启动新的线程;

②.使用start()是启动新的线程,通过新的线程间接执行run中的代码;

4.3.sleep与yield

1>.sleep说明

①.调用sleep会让当前线程从Running(运行)进入Timed Waiting状态(阻塞);

②.其它线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException;

③.睡眠结束后的线程未必会立刻得到执行,需要重新分配CPU时间片;

④.建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性;

  • TimeUnit.单位名称.sleep(long n);

2>.yield说明

①.调用yield会让当前线程从Running运行状态进入Runnable就绪状态,然后调度执行其它线程;

②.具体的实现依赖于操作系统的任务调度器;

  • 有的时候即使调用了yield()方法,但是该方法并不会立刻执行!

注意: 任务调度器会把CPU时间片分配给就绪状态(/可运行状态)的线程,但是不会把CPU时间片分配给阻塞状态的线程;

3>.案例: 防止CPU占用100%

在没有利用CPU进行计算时,不要让while(true),for(;;)等死循环空转浪费CPU,这时可以使用yield或者sleep来让出CPU执行权给其它线程/程序;

while(true) {
   try {
      Thread.sleep(50);
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}

单核CPU环境下效果更加明显!!

4.4.线程优先级

1>.线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它;

2>.如果CPU比较忙,那么优先级高(数值越大)的线程会获得更多的时间片,但CPU闲时,优先级几乎没作用;

例如:

@Slf4j
public class TestPriority {
    public static void main(String[] args) {
        //创建一个线程
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    log.info("线程t1执行任务..." + i);
                }
            }
        };

        //创建一个线程
        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    log.info("线程t2执行任务..." + i);
                }
            }
        };

        //设置线程优先级(必须是在线程执行start()方法之前设置线程优先级)
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);

        //启动线程
        t1.start();
        t2.start();
    }
}

在这里插入图片描述

4.5.join方法

问题: 为什么需要join?

案例:

@Slf4j
public class TestJoin {
    static int r = 0;

    public static void main(String[] args) {
        test1();
    }

    public static void test1() {
        log.info("主线程开始执行...");
        Thread t1 = new Thread(() -> {
            log.info("子线程开始执行...");
            try {
                //子线程执行到这里的时候,会让出CPU执行权,暂停执行
                //此时变量r的值还是0
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("子线程执行结束...");

            r = 10;
        }, "t1");

        t1.start();

        //子线程交出CPU执行权,暂停执行
        //主线程获取CPU时间片继续执行,此时变量r的值还是0
        log.info("结果为:{}", r); //0
        log.info("主线程执行结束...");

        //等到主线程执行完毕,或者CPU时间片用完,子线程继续执行
        //此时变量r的值才会变成10
    }
}

在这里插入图片描述
可以看到控制台并没有输出我们想要的结果!

如何主线程打印变量r的最终结果或者说是子线程计算之后的结果?
①.用sleep行不行?为什么?

  • 使用sleep()也可以实现,但是主线程休眠时候不好控制,一般来说要大于子线程休眠时间+子线程业务处理时间;

②.用join,加在t1.start()之后即可!

代码如下

@Slf4j
public class TestJoin {
   static int r = 0;

   public static void main(String[] args) throws InterruptedException {
       test1();
   }

   public static void test1() throws InterruptedException {
       log.info("主线程开始执行...");
       Thread t1 = new Thread(() -> {
           log.info("子线程开始执行...");
           try {
               //子线程执行到这里的时候,会让出CPU执行权,暂停执行
               //此时变量r的值还是0
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           log.info("子线程执行结束...");

           r = 10;
       }, "t1");

       t1.start();

       //等待线程t1代码执行完毕,后面(主线程)的代码才能继续执行
       t1.join();

       //子线程交出CPU执行权,暂停执行
       //主线程获取CPU时间片继续执行,此时变量r的值还是0
       log.info("结果为:{}", r); //0
       log.info("主线程执行结束...");

       //等到主线程执行完毕,或者CPU时间片用完,子线程继续执行
       //此时变量r的值才会变成10
   }
}

在这里插入图片描述

4.6.interrupt方法

4.6.1.打断调用了sleep(),wait(),join()方法处于阻塞状态的线程

sleep(),wait(),join()这几个方法都会让线程进入阻塞状态;

打断阻塞状态的线程,会清空打断状态,也就是说,通过interrupt()方法打断处于阻塞状态的线程,最终会以抛出异常的方式清空/还原打断标记,以sleep()方法为例:

@Slf4j
public class TestInterrupt {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            log.info("sleep...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");

        t1.start();

        Thread.sleep(1000);

        log.info("interrupt...");
        t1.interrupt(); //处于阻塞状态的线程被打断,抛出异常,线程还可以继续执行后面的代码!

        //打断标记默认值为false,当线程调用sleep(),wait(),join()方法处于阻塞状态
        //而后又调用了interrupt()方法打断阻塞状态,此时打断标记就变成了true,
        //但是sleep(),wait(),join()方法最终会通过抛出异常的方式清空打断标记
        //因此打断标记最终的值又恢复到最初的false!!!
        log.info("打断标记:{}", t1.isInterrupted()); //false
    }
}

在这里插入图片描述

4.6.2.打断正常运行状态的线程

打断正常运行的线程,不会清空打断状态,也就是说,通过interrupt()打断正在运行的线程时,打断标记不会被清空!!

@Slf4j
public class TestInterrupt2 {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            //让线程一直处于运行状态
            while (true) {
                //根据打断标记来终止线程的运行
                if (Thread.currentThread().isInterrupted()){
                    log.info("线程被打断,退出运行!");
                    break;
                }
            }
        }, "t1");

        t1.start();

        Thread.sleep(1000);

        log.info("interrupt...");
        //注意:调用interrupt()方法打断正在运行的线程,线程并不会停止,而是继续循环运行!!!
        t1.interrupt();
        log.info("打断标记:{}",t1.isInterrupted()); //true
    }
}

在这里插入图片描述

4.6.3.打断park线程

打断park线程,不会清空打断状态,即打断标记不会恢复到原始状态;

park()函数是将当前调用线程阻塞,而unpark()函数则是将指定线程唤醒,但是要注意的是如果线程的打断标记为true,那么再执行park()函数是没有效果的;

与Object类的wait/notify机制相比,park/unpark有两个优点:

  • 以thread为操作对象更符合阻塞线程的直观定义;
  • 操作更精准,可以准确地唤醒某一个线程(notify随机唤醒 一个线程,notifyAll唤醒所有等待的线程),增加了灵活性;
@Slf4j
public class TestInterrupt3 {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            log.info("park...");
            //线程阻塞
            LockSupport.park();

            log.info("unpark...");
            log.info("打断状态:{}",Thread.currentThread().isInterrupted());

            //此时线程的打断状态为true,那么再执行park()函数是没有效果的!!
            //如果想要再次调用park()函数阻塞线程,必须要将线程的打断状态恢复到原始状态(interrupt()/interrupted())
            //LockSupport.park();
            //log.info("unpark...");
        },"t1");

        t1.start();

        Thread.sleep(1000);
        t1.interrupt();
    }
}

4.7.不推荐使用的方法

这些方法已经过时,容易破坏同步代码块,造成线程死锁;
在这里插入图片描述

5.主线程和守护线程

1>.默认情况下,Java进程需要等待所有线程都运行结束,Java进程才会结束.有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束/终止;

Java程序中的线程默认都是非守护线程!!!

例如:

@Slf4j
public class TestThread1 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    break;
                }
            }

            log.info("线程结束");
        },"t1");
        //将t1线程设置为守护线程,等到其他非守护线程执行完毕之后,该守护线程会强制结束/终止!
        t1.setDaemon(true);
        t1.start();

        Thread.sleep(2000);
        log.info("程序结束!");
    }
}

在这里插入图片描述

注意:

①.垃圾回收器线程就是一种守护线程;
②.Tomcat中的Acceptor和Poller线程都是守护线程,因此Tomcat在接收到shutdown命令之后,不会等待他们处理完当前的请求;

6.线程的状态

6.1.五种状态(操作系统层面)

在这里插入图片描述
说明:

①.初始状态: 仅是在语言层面创建了线程对象(new,没有调用start()),还未与操作系统线程关联;
②.可运行状态(/就绪状态): 指该线程已经被创建(调用了start(),与操作系统线程关联),可以由CPU调度执行;
③.运行状态: 指获取了CPU时间片处于运行中的状态;

  • 当CPU时间片用完,会从[运行状态]转换至[可运行状态],会导致线程的上下文切换;

④.阻塞状态:

  • 如果线程中调用了阻塞API,如BIO读写文件,这时该线程实际不会用到CPU,会导致线程上下文切换,进入"阻塞状态";(线程会让出CPU的使用权);
  • 等BIO操作完毕,会由操作系统唤醒处于"阻塞状态"的线程,线程转换至"可运行状态";
  • 与"可运行状态"的区别是,对于处在"阻塞状态"的线程来说只要它们一直不被唤醒,调度器就一直不会考虑调度它们;

⑤.终止状态: 表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态;

6.2.六种状态(Java API层面)

根据Thread.State枚举类,分为六种状态:
在这里插入图片描述
说明:

①.NEW状态: 线程刚被创建,但是还没有调用start()方法;
②.RUNNABLE状态: 当调用了start()方法之后,注意Java API层面的RUNNABLE状态涵盖了操作系统层面的"可运行状态"、“运行状态"和"阻塞状态”(由于BIO导致的线程阻塞,在Java里无法区分,仍然认为是可运行的);
③.BLOCKED,WAITING,TIMED_WAITING状态都是Java API层面对"阻塞状态"的细分;

  • 在多线程环境下,多个线程竞争锁资源,没有获取到锁的线程就会处于"BLOCKED阻塞状态";
  • 在多线程环境下,如果在某个线程中调用了另外一个死循环执行的线程的join()方法,那么该线程就会处于"waiting阻塞状态";

④.TERMINATED状态: 当线程代码运行结束;

注意:

在开发过程中,在开发工具中进行调试的时候,开发工具中显示的线程状态和上面提到的线程状态名称可能并不相同(WAITING=WAIT,BLOCKED=MONITOR,RUNNABLE=RUNNING),但是请相信,两者都是正确的!

示例代码

@Slf4j
public class TestThreadStates {

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            log.info("t1 running...");
        }, "t1");

        Thread t2 = new Thread(() -> {
            while (true) {

            }
        }, "t2");
        t2.start();

        Thread t3 = new Thread(() -> {
            log.info("running...");
        }, "t3");
        t3.start();

        Thread t4 = new Thread(() -> {
            synchronized (TestThreadStates.class) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t4");
        t4.start();

        Thread t5 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t5");
        t5.start();

        Thread t6 = new Thread(() -> {
            synchronized (TestThreadStates.class){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t6");
        t6.start();

        Thread.sleep(500);

        log.info("t1 state {}",t1.getState());
        log.info("t2 state {}",t2.getState());
        log.info("t3 state {}",t3.getState());
        log.info("t4 state {}",t4.getState());
        log.info("t5 state {}",t5.getState());
        log.info("t6 state {}",t6.getState());
    }
}

在这里插入图片描述

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

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

相关文章

基于JAVA springboot + MYSQL +VUE的项目管理系统(含数据库),包括工时统计、原型预览、效果图管理等

平台介绍 无鱼工时管理系统&#xff0c;是一款轻量级工时记录和管理工具&#xff0c;包括项目管理&#xff0c;工时上报&#xff0c;工时日报&#xff0c;工时统计等功能。 无鱼工时管理系统可通过员工工时上报的方式&#xff0c;来记录项目所花费的工时&#xff0c;帮助企业…

滑块验证 - 使用AJ-Captcha插件【超简单.jpg】

滑块验证实现一、后端1&#xff09;首先引入maven&#xff1a;2&#xff09;再在application.yml中自定义水印&#xff0c;直接启动后前端就可以请求接口了3&#xff09;重写CaptchaCacheServiceRedisImpl①先新建一个文件夹②重写impl二、前端&#xff1a;1&#xff09;复制文…

UML2面向对象分析与设计(第2版) 谭火彬 杂记

首先&#xff0c;来讲讲我对泛化的理解&#xff0c;其实这是站在的视角的不同而表述的不同&#xff0c;泛化是站在父类的角度&#xff0c;父类给孩子的方式叫泛化&#xff0c;而继承是站在孩子的角度&#xff0c;儿子继承父类的方式叫继承。 其实上了谭老师大概一章的课程&…

使用WebPageTest、Lighthouse和Chrome DevTools评估网站性能

目录 一&#xff1a;使用WebPageTest评估网站性能 二&#xff1a;使用Lighthouse分析性能 1、本地npm安装Lighthouse 2、Chrome DevTools中使用 三&#xff1a;使用Chrome DevTools分析性能 一&#xff1a;使用WebPageTest评估网站性能 进入网站首页WebPageTest - Websit…

Linux下常用基本指令大全

在XShell下的复制粘贴 复制: ctrl insert (有些insert 需要配合fn 来按) 粘贴: shift insert ctrl c / ctrl v 是不行的.1. ls指令 语法&#xff1a;ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0…

从App Lab就一鸣惊人的Gorilla Tag,创始人竟是个电竞选手

前不久&#xff0c;热门VR游戏《Gorilla Tag》登陆Quest正式商店&#xff0c;此前该作已经进入App Lab和SteamVR平台&#xff0c;一度成为App Lab最受欢迎的游戏&#xff0c;至此终于修成正果。截至目前&#xff0c;该作在全平台的下载量高达500万次&#xff0c;在Quest商店累计…

七、Java 14 新特性

七、Java 14 新特性 Java 14 已如期于 2020 年 3 月 17 日正式发布&#xff0c;此次更新是继半年前 Java 13 这大版本发布之后的又一次常规版本更新&#xff0c;即便在全球疫情如此严峻形势下&#xff0c;依然保持每六个月的版本更新频率&#xff0c;为大家及时带来改进和增强&…

覆盖和覆盖D2D通信网络的传输容量分析(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 移动数据流量的日益增长与有限的频谱资源之间的矛盾催生了用以提升频谱空间利用率的设备到设备&#xff08;Device-to-Device,…

Typora配置github图床踩坑记录:“success“:false,“message“:“upload error

今天在给typora配置图床的时候遇到了一丢丢问题 我是根据这位大佬的配置步骤使用GithubpicGo搭建图床&#xff0c;保姆级教程来了一步步来的&#xff0c;在处理typora验证图片上传选项时遇到问题&#xff1a; {“success”:false,“message”:“upload error. see C:\Users\AS…

【JavaScript】时间对象

基础知识&#xff1a; 获取年月日&#xff0c;时间&#xff1a; var datenew Date();console.log(date);Thu Nov 17 2022 10:15:07 GMT0800 (中国标准时间) 实际上是对象&#xff0c;但是自动转为字符串显示了。 // 传递参数进去 var date1new Date("2022/11/25 14:38…

录屏软件哪个好?五款免费视频录屏软件

现如今在日常生活中都可以使用得上录屏软件&#xff0c;比如线上会议直播、线上教程课程、网课直播、玩游戏精彩瞬间等等&#xff1b;在互联网众多推荐下&#xff0c;到底录屏软件哪个好&#xff1f;今天小编在这里给大家分享五款免费视频录屏软件&#xff0c;有需要的朋友可以…

个人的一些小创作(随便玩玩,由于技术限制,无使用价值)

tips 1. 桶排序算法的话&#xff0c;必须会去重&#xff0c;因为它的核心就是把数值与数组的下标对应起来&#xff0c;那如果你有多个相同的数值&#xff0c;那也只能对应同一个数组的下标。然后桶排序算法最后打印的是数组的下标&#xff0c;而不是数组下标对应元素的值。 2…

wps合并多个pdf,四个步骤轻松解决

wps这个软件相信大家都或多或少使用过。很多人日常触及到wps的功能&#xff0c;一般是word、Excel或者PPT这三种。其实wps也可以用来读取、编辑PDF文档。wps编辑PDF文档需要一定的技巧&#xff0c;比如使用wps合并多个pdf的方法&#xff0c;很多网友都在问。这里给大家做一个详…

10、SpringCloud 系列:Nacos - 注册中心

SpringCloud 系列列表&#xff1a; 文章名文章地址01、Eureka - 集群、服务发现https://blog.csdn.net/qq_46023503/article/details/12831902302、Ribbon - 负载均衡https://blog.csdn.net/qq_46023503/article/details/12833228803、OpenFeign - 远程调用https://blog.csdn.…

41. 使用块的网络(VGG)代码实现

1. VGG块 在下面的代码中&#xff0c;我们定义了一个名为vgg_block的函数来实现一个VGG块。 该函数有三个参数&#xff0c;分别对应于卷积层的数量num_convs、输入通道的数量in_channels 和输出通道的数量out_channels. import torch from torch import nn from d2l import …

【玩转c++】c++ :string类讲解(万字详解)

目录 &#x1f341;1. 为什么要学习string类 &#x1f341;2. 标准库中的string类 &#x1f341;3. string类各种接口 默认成员函数 Iterators迭代器 capacity容量 Element access:元素访问 Modifiers:修改 字符串操作 成员变量 非成员函数 &#x1f341;4. 扩展阅读 本期主题…

stm32f407VET6 系统学习 day07 通用定时器, OLED 屏幕使用 PWM 的使用

1. 通用定时器的知识 1.STM32共有14个定时器&#xff0c;其中12个16位定时器&#xff0c;2个32 位定时器 2. 通用定时器特点 1. 16/32位向上、向下、向上/向下(中心对齐)计数模式&#xff0c;自动装载计数器&#xff08;TIMXCNT) 。 2. 16位可编程预分频器(TIMx_PSC)&…

-bash: lsof: command not found解决办法

简言 centos系统&#xff0c;检测端口时使用lsof命令发现lsof功能未开启&#xff0c;如下图 [rootiZwz9501p9hnysn92hpx27Z tnt_game]# lsof -bash: lsof: command not found 安装lsof centos系统下可以直接使用yum安装lsof功能&#xff0c;如下图 yum可自动完成安装lsof ls…

gitlab-ci.yml关键字(一)image、variables、include

image 这是一个全局关键字&#xff0c;如果流水线的执行器是使用docker来运行的话&#xff0c;那可以指定docker中的docker镜像。如果执行器是shell的话&#xff0c;那该关键字是无用的&#xff0c;即便机器中已近安装了docker的环境&#xff0c;该关键字可以在全局或者某一个…

NeurIPS2021 | ViTAE+: vision transformer中的归纳偏置探索

参考资料&#xff1a;NeurIPS 2021 | ViTAE: vision transformer中的归纳偏置探索 - 知乎 paper地址&#xff1a;https://openreview.net/pdf?id_RnHyIeu5Y5 论文标题&#xff1a;ViTAE: Vision Transformer Advanced by Exploring Intrinsic Inductive Bias code&#xff…