Java基础(进程与线程,线程的创建)

Unity海洋渲染实战:Ceto Ocean System核心原理与高级应用指南 实时水面渲染是计算机图形学中的重要课题,涉及波浪模拟、光照交互和着色器编程等核心技术。其原理通常基于物理模型,如使用Gerstner波模拟规则海浪,或通过快速傅里叶变换(FFT)处理频谱数据生成随机海面。这些技术的价值在于能以可控的性能开销,在游戏中实现具有体积感、反射和折射效果的真实水体。在开放世界、海战模拟等应用场景中,专业的海洋系统如Ceto Ocean System,通过模块化架构整合了波浪生成、动态网格和渲染管线,为开发者提供了完整的解决方案。本文深入探讨了其双引擎波浪系统和多层渲染结构,并分享了 阅读详情

1 进程和线程

1.1 进程

在计算机中,进程代表了内存中正在运行的应用程序,计算机中的资源(cpu 内存 磁盘 网络等),会按照需求分配给每个进程,从而这个进程对应的应用程序就可以使用这些资源了。

在这里插入图片描述

在操作系统中,启动一个应用程序的时候,会有一个或多个进程同时被创建,这些进程其实就表示了当前这个应用程序,在系统中的资源使用情况以及程序运行的情况。如果关闭这个进程,那么对应的应用程序也就关闭了。

所以,进程就是在系统中,运行一个应用程序的基本单位。

1.2 线程

线程是进程中的一个代码执行单元,负责当前进程中代码程序的执行,一个进程中有一个或多个线程。

当一个进程中启动了多个线程去分别执行代码的时候,这个程序就是多线程程序。

在这里插入图片描述

例如,当前我们使用java命令去运行一个类的时候,会先启动JVM,这个JVM对于计算机来讲,就是一个应用程序,所以同时系统中也会启动一个进程和这个JVM对应。

在桌面新建文件Hello.java

public class Hello{
	
	public static void main(String[] args)throws Exception{
		
		System.out.println("hello");
        
		long time = 1000*100L;
		Thread.sleep(time);
		
		System.out.println("world");
	}

}

Thread.sleep方法,可以让当前执行代码的线程,暂时的休眠一会

使用命令编译运行Hello类:java Hello

注意,代码中,输出hello之后,JVM并没有直接结束,而是让当前线程去休眠了100秒,所以这个时候JVM还在运行着,我们可以在任务管理中,看到JVM对应的进程了

在这里插入图片描述

如果把这个进程强行关闭掉,那么JVM就停止了,那么程序的运行也就停止了。

注意,可以先关闭STS等开发工具,因为很工具运行时候的也需要启动一个JVM,所以这里可能会出现多个进程

除此之外,我们还可以使用JDK中提供的工具,来查看JVM当前的运行情况:

这里通过JDK自带的jconsole工具,可以检测到当前运行Hello这个类的时候,JVM的运行情况,包含内存的使用、线程的运行状态、类的加载等信息

在这里插入图片描述


例如,查看当前JVM中执行main方法线程信息

在这里插入图片描述

注意,这线程的名字就叫main,它是任务就是调用执行我们类中的main方法!

所以,是一个名字叫main的线程,调用执行我们代码中的main方法

2 并发和并行

  • 线程的并发执行,是指在一个时间段内,俩个或多个线程,使用一个CPU,进行交替运行。

  • 线程的并行执行,是指在同一时刻,俩个或多个线程,各自使用一个CPU,同时进行运行。

如果计算机是单核CPU的话,那么同一时刻只能有一个线程使用CPU来执行代码

如果计算机是多核CPU的话,那么同一时刻有可能是俩个线程同时使用不同的CPU执行代码

可以看出,多核CPU确定可以提供程序的运行速度。

但是,假如我们在程序中编写了俩个线程,启动并运行它们,那么我们是无法控制也无法知道,计算机中到底是使用了一个CPU去运行它们,还是使用俩个CPU去运行它们。这是计算机内核中对资源进行调度的事情,我们从应用程序的层面是无法干涉的。

所以,在一般情况下,我们编写多线程代码时,可以用单核CPU的情况来考虑问题,去设计并编写多线程的代码。

将来计算机如果真的使用多个CPU来运行我们的多线程代码的话,那么也只是提高了代码的执行效率,基本不会影响我们代码的设计和实现的。

3 时间片

3.1 概述

时间片,当前一个线程要使用CPU的时候,CPU会分配给这个线程一小段时间(毫秒级别),这段时间就叫做时间片,也就是该线程允许使用CPU运行的时间,在这个期间,线程拥有CPU的使用权。

如果在一个时间片结束时,线程还在运行,那么这时候,该线程就需要停止运行,并交出CPU的使用权,然后等待下一个CPU时间片的分配。

所以,在宏观上,一段时间内,我们感觉俩个线程在同时运行代码,其实在微观中,这俩个线程在使用一个CPU的时候,它们是交替着运行的,每个线程每次都是运行一个很小的时间片,然后就交出CPU使用权,只是它们俩个交替运行的速度太快了,给我们的感觉,好像是它们俩个线程在同时运行。

思考,生活中还有哪些是因为速度太快,从而通过我们的眼睛“欺骗”了我们的情况?

3.2 调度

当俩个或多个线程使用一个CPU来运行代码的时候,在操作系统的内核中,就会有相应的算法来控制线程获取CPU时间片的方式,从而使得这些线程可以按照某种顺序来使用cpu运行代码,这种情况被称为线程调用。

常见的调度方式:

  • 时间片轮转

    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

  • 抢占式调度

    系统会让优先级高的线程优先使用 CPU(提高抢占到的概率),但是如果线程的优先级相同,那么会随机选择一个线程获取当前CPU的时间片。

JVM中的线程,使用的为抢占式调度。

例如,

public static void main(String[] args) {
	
    //创建线程对象t1
    Thread t1 = new Thread(){
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("hello");
            }
        }
    };
	
    //创建线程对象t2
    Thread t2 = new Thread(){
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("world");
            }
        }
    };
	
    //启动线程t1 t2
    t1.start();
    t2.start();


}

代码中,线程t1中循环输出100次hello,线程t2中循环输出100次world,启动t1和t2后,它们就开始公平的竞争(默认情况下优先级相等),抢占CPU的时间片(使用权),抢到之后就可以使用cpu运行代码了,所以可以看到最后的运行结果,hello和world是交替运行的,但是每次的运行结果都会有所不同。

4 main线程

使用java命令来运行一个类的时候,首先会启动JVM(进程),JVM会在创建一个名字叫做main的线程,来执行类中的程序入口(main方法)

例如,

public class Test {

    public static void main(String[] args) {

        //获取执行当前方法的线程对象
        Thread currentThread = Thread.currentThread();
        System.out.println("执行当前方法的线程名字为:"+currentThread.getName());

    }

}
//运行结果:
执行当前方法的线程名字为:main

所以,我们写在main方法中的代码,其实都是由名字叫做main的线程去执行的

Thread.currentThread();可以写在任意方法中,返回就是执行这个方法的线程对象

上面代码使用java命令运行的过程是:

在这里插入图片描述

  1. 使用java命令运行Test类,会先启动JVM

  2. 应用类加载器通过CLASSPATH环境变量配置的路径,找到Test.class文件,并加载到方法区。

    注意,这里会同时生产一个Class类型对象,来代表这个Test类型,并且会优先处理类中的静态代码(静态属性、静态方法、静态代码块)

  3. JVM创建并启动一个名字叫做main的线程

  4. main线程将Test中的main方法加载到栈区中

  5. 在栈里面,main线程就可以一行行的执行方法中的代码了

  6. 如果在执行代码中,遇到了方法调用,那么线程会继续把被调用的方法,加载到栈中(压栈操作),然后执行栈顶这个最新添加进来的方法,栈顶方法执行完,就释放(出栈操作),然后在进行执行当前最新的栈顶方法(之前我们画过栈里面的方法调用图,例如在异常的学习过程中)

  7. 代码执行过程输出执行结果

  8. 当前是单线程程序,main线程结束了,JVM就停止了,如果是多线程程序,那么JVM要等所有线程都结束了才会停止

5 线程的创建和启动

java.lang.Thread 是java中的线程类,所有的线程对象都必须是Thread类或其子类的实例。

每个线程的作用,就是完成我们给它指定的任务,实际上就是执行一段我们指定的代码。我们只需要在Thread类的子类中重写run方法,把执行的代码写入到run方法中即可,这就是线程的执行任务!

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

  1. 定义Thread类的子类(可以是匿名内部类),并重写Thread类中的run方法,run方法中的代码就是线程的执行任务
  2. 创建Thread子类的对象,这个对象就代表了一个要独立运行的新线程
  3. 调用线程对象的start方法来启动该线程

例如,

public class Test {

    public static void main(String[] args) {
		
        //2.创建线程类对象
        Thread t = new MyThread();
        //3.调用start方法启动线程
        t.start();

    }


}

//1.子类继承父类Thread,并重写run方法(指定线程的执行任务)
class MyThread extends Thread{
    @Override
    public void run() {

        for (int i = 0; i < 10; i++) {
            System.out.println("hello world");
            try {
                //可以让当前执行代码的线程睡眠1000毫秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

根据结果,可以看出,t线程启动开始执行的时候,每输出一次hello world都会睡眠1秒钟

例如,也可以使用匿名内部类的形式来完成

public class Test {

    public static void main(String[] args) {

        Thread t = new MyThread(){
            @Override
            public void run() {

                for (int i = 0; i < 10; i++) {
                    System.out.println("hello world");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        };
        
        t.start();

    }


}

在此过程中,main线程和t线程之间的关系是:
在这里插入图片描述

可以看出,main线程在执行main方法的过程中,创建并启动了t线程,并且t线程启动后,和main线程就没有关系了,这时候main线程和t线程都是自己独立的运行,并且他们俩个是要争夺CUP的时间片(使用权)的

以上代码在内存中的情况:

在这里插入图片描述

注意1,之前所提到的栈区,又被称为方法调用栈,是线程专门执行方法中代码的地方,并且每一个线程,都有自己独立的栈空间,和别的线程相互不影响

注意2,最先启动的线程是主线程(main线程),因为它要执行程序的入口main方法,在主线程中,创建并且启动了t线程,启动之后main线程和t线程将各自独立运行,并且争夺CPU的时间片

注意3,线程启动之后(调用start方法),会开始争夺CPU的时间片,然后自动执行run方法,如果子类对象重写了,那么就调用到重写后的run方法

注意4,堆区是对所以线程共享的,每个线程中如果创建了对象,那么对象就会存放到堆区中

注意5,线程对象t被创建出来的时候,它还只是一个普通的对象,但是当调用了t.start()方法之后,线程对象t可以说才真正的“现出原形”:开辟了单独的栈空间,供线程t调用方法使用

思考,多线程比单线程的优势在哪里?我们一般会把什么样的代码交给多先去执行处理?

6 Runnable接口

给一个线程对象指定要执行的任务,除了继承Thread类后重写run方法之外,还可以利于Runnable接口来完成线程任务的指定

java.lang.Runnable,该接口中只有一个抽象方法run

public interface Runnable {
   public abstract void run();
}

其实Thread类也是Runnable接口的实现类,其代码结构大致为:

public class Thread implements Runnable {
    /* What will be run. */
    private Runnable target;
    
    public Thread() {
        //...
    }
    
    public Thread(Runnable target) {
        this.target = target;
        //..
    }
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

可以看出,子类重写Thread中的run方法,这个run方法其实也来自于Runnable接口

通过以上的代码结构,可以知道,我们还可以直接创建Thread对象,在调用构造器的时候,传一个Runnable接口的实现类对象进来,然后调用线程的对象run方法,那么默认就会调用到Runnable接口实现类重写的run方法!

例如,使用Runnable接口的匿名内部类,来指定线程的执行任务(重写接口中的run方法)

public class Test {

    public static void main(String[] args) {
		
        //Runnable接口的实现类中,重写了run方法,指定线程的执行任务
        Runnable run = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("hello world");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
		
        //创建线程对象,指定执行任务
        Thread t = new Thread(run);

        t.start();

    }

}

可以看出,这种方式也可以完成之前相同的功能

实现Runnable接口比继承Thread类所具有的优势:

  1. 可以把相同的一个执行任务(Runnable接口的实现),交给不同的线程对象去执行
  2. 可以避免java中的单继承的局限性。
  3. 线程和执行代码各自独立,实现代码解耦

7 线程的名字

通过Thread类中的currentThread方法,可以获取当前线程的对象,然后调用线程对象的getName方法,可以获取当前线程的名字。String name = Thread.currentThread().getName();

注意,这里说的当前线程,指定是执行当前方法的线程,因为获取线程名字的代码肯定是写在某个方法中的,并且这个方法一定是由某个线程调用执行的,它们的关系如下:

例如,

public class Test {

    public static void main(String[] args) {

        String name = Thread.currentThread().getName();
        System.out.println("执行当前main方法的线程是:"+name);


        Runnable run = new Runnable() {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                System.out.println("执行当前run方法的线程是:"+name);
            }
        };

        Thread t = new Thread(run);

        t.start();

    }

}
//运行结果为:
执行当前main方法的线程是:main
执行当前run方法的线程是:Thread-0

注意,一定要记得,start方法启动线程后,线程会自动执行run方法

千万不要直接调用run方法,这样就不是启动线程执行任务,而是普通的方法调用,和调用sayHello没区别

默认情况下,主线程中,创建出的线程,它们的都会有一个默认的名字:

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

其中,"Thread-" + nextThreadNum() 就是在拼接出这个线程默认的名字,Thread-0 Thread-1 Thread-2等等

我们也可以创建线程对象的时候,给它设置一个指定的名字:

Thread t = new Thread("t线程");

//或者
Thread t = new Thread(new Runnable(){
    public void run(){
        //执行任务
    }
},"t线程");


//或者
Thread t = new Thread();
t.setName("t线程");

8 线程的分类

java中,线程可以分为:

  • 前台线程,又叫做执行线程、用户线程
  • 后台线程,又叫做守护线程、精灵线程

前台线程:

这种线程专门用来执行用户编写的代码,地位比较高,JVM是否会停止运行,就是要看当前是否还有前台线程没有执行完,如果还剩下任意一个前台线程没有“死亡”,那么JVM就不能停止!

例如,执行程序入口的主线程(main),就是一个前台线程,在单线程程序中,main方法执行完,就代表main线程执行完了,这时候JVM就停止了

例如,我们在主线程创建并启动的新线程,默认情况下就是一个前台线程,用来执行用户编写的代码任务。

后台线程:

这种线程是用来给前台线程服务的,给前台线程提供一个良好的运行环境,地位比较低,JVM是否停止运行,根本不关心后台线程的运行情况和状态。

例如,垃圾回收器,其实就一个后台线程,它一直在背后默默的执行着负责垃圾回收的代码,为我们前台线程在执行用户代码的时候,提供一个良好的内存环境。

思考,前台线程和后台线程的关系,在生活中是否也存在类似的例子呢?

在主线程中,创建出来的线程对象,默认就是前台线程,在它启动之前,我们还可以给它设置为后台线程:

public class Test {

    public static void main(String[] args) {


        Thread t = new Thread("t线程"){
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 10; i++) {
                    System.out.println(name+": hello "+i);
                }
            }
        };
		
        //在启动线程之前,可以将其设置为后台线程,否则默认是前台线程
        t.setDaemon(true);

        t.start();


    }

}

注意,t.setDaemon(true);这句代码,注释掉和不注释,观察代码的运行结果,看是否一样?

9 线程优先级

线程类Thread中,有一个属性,表示线程的优先级,代码结果大致为:

public class Thread implements Runnable {
    private int	priority;
    
    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    
    public final int getPriority() {
        return priority;
    }
    
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }
    
    private native void setPriority0(int newPriority);
    
}

可以看出,最终设置线程优先级的方法,是一个native方法,并不是java语言实现的

线程的优先级使用int类型数字表示,最大是10,最小是1,默认的优先级是5。

当俩个线程争夺CPU时间片的时候:

  • 优先级相同,获得CPU使用权的概率相同
  • 优先级不同,那么高优先级的线程有更高的概率获取到CPU的使用权

例如,t1和t2线程各自运行10000次循环,看哪个线程先运行完

public class Test {

    public static void main(String[] args) {


        Thread t1 = new Thread("t1线程"){
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 10000; i++) {

                }
                System.out.println(name+"线程执行完毕");
            }
        };

        Thread t2 = new Thread("t2线程"){
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 10000; i++) {

                }
                System.out.println(name+"线程执行完毕");
            }
        };

//        t1.setPriority(Thread.MAX_PRIORITY);
//        t2.setPriority(Thread.MIN_PRIORITY);

        System.out.println("t1线程的优先级:"+t1.getPriority());
        System.out.println("t2线程的优先级:"+t2.getPriority());


        t1.start();
        t2.start();


    }

}

注意1,默认情况下,俩个线程的优先级都是5,那个俩个线程争夺到CPU的使用权的概率一样,那么基本上俩个线程都有相同的概率先执行完10000次循环

注意2,其实t1先稍微占了那么一点点的优势,因为毕竟在主线程的代码中,先启动了t1先,然后又启动了t2线程

注意3,设置t1和t2优先级的之后,在运行查看结果,会明显看到优先级高的线程,有更高的概率先执行完代码

10 线程组

Java中使用java.lang.ThreadGroup类来表示线程组,它可以对一批线程进行管理,对线程组进行操作,同时也会对线程组里面的这一批线程操作。

java.lang.ThreadGroup:

public class ThreadGroup{
    public ThreadGroup(String name){
    	//..
    }
	public ThreadGroup(ThreadGroup parent, String name){
        //..
    }
}

创建线程组的时候,需要指定该线程组的名字。

也可以指定其父线程组,如果没有指定,那么这个新创建的线程组的父线程组就是当前线程组。

例如,

public class Test {

    public static void main(String[] args) {


        //获取当前线程对象
        Thread currentThread = Thread.currentThread();

        //获取当前线程所属的线程组
        ThreadGroup currentThreadGroup = currentThread.getThreadGroup();

        System.out.println(currentThreadGroup);

    }

}
//运行结果:
java.lang.ThreadGroup[name=main,maxpri=10]

可以看出,当前线程组的名字为main,并且线程组中的线程最大优先级可以设置为10

例如,用户在主线程中创建的线程,属于默认线程组(名字叫"main"的线程组)

public class Test {

    public static void main(String[] args) {


        Thread t = new Thread();

        ThreadGroup threadGroup = t.getThreadGroup();

        System.out.println(threadGroup);


    }

}
//运行结果:
java.lang.ThreadGroup[name=main,maxpri=10]

可以看出,主线程中,创建一个线程对象,它的线程组默认就是当前线程的线程组

例如,

public class Test {

    public static void main(String[] args) {


        ThreadGroup group = new ThreadGroup("我的线程组");
		
        //指定线程所属的线程组
        Thread t = new Thread(group,"t线程");

        ThreadGroup threadGroup = t.getThreadGroup();

        System.out.println(threadGroup);


    }

}

//运行结果:
java.lang.ThreadGroup[name=我的线程组,maxpri=10]

例如,

public class Test {

    public static void main(String[] args) {


        ThreadGroup group = new ThreadGroup("我的线程组");

        Runnable run = new Runnable() {
            @Override
            public void run() {
                try {
                    //让线程休眠一会,否则运行太快,死亡太快了
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t1 = new Thread(group,run,"t1线程");
        Thread t2 = new Thread(group,run,"t2线程");
        Thread t3 = new Thread(group,run,"t3线程");

        //注意,启动后,三个线程都会进行休眠,等run方法运行完就“死亡”了
        t1.start();
        t2.start();
        t3.start();

        //返回当前线程组中还没有“死亡”的线程个数
        System.out.println("线程组中还在存活的线程个数为:"+group.activeCount());

        //准备好数组,保存线程组中还存活的线程
        Thread[] arr = new Thread[group.activeCount()];

        //将存活的线程集中存放到指定数组中,并返回本次存放到数组的存活线程个数
        System.out.println("arr数组中存放的线程个数为:"+group.enumerate(arr));

        //输出数组中的内容
        System.out.println("arr数组中的内容为:"+Arrays.toString(arr));


    }

}
//运行结果:
线程组中还在存活的线程个数为:3
arr数组中存放的线程个数为:3
arr数组中的内容为:[Thread[t1线程,5,我的线程组], Thread[t2线程,5,我的线程组], Thread[t3线程,5,我的线程组]]
    

注意,只有在创建线程对象的时候,才能指定其所在的线程组,线程运行中途不能改变它所属的线程组

11 线程状态

java.lang.Thread.State枚举类型中(内部类形式),定义了线程的几种状态,其代码结果为:

public class Thread{
    /* Java thread status for tools,
     * initialized to indicate thread 'not yet started'
     */
    private volatile int threadStatus = 0;
    
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
    
    //返回这个线程当前所处的状态
    public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }
    
}

状态描述和解释如下:

线程状态名称描述
NEW新建线程刚被创建,还没调用start方法,或者刚刚调用了start方法,调用start方法不一定"立即"改变线程状态,中间可能需要一些步骤才完成一个线程的启动。
RUNNABLE可行行start方法调用结束,线程由NEW变成RUNNABLE,线程存活着,并尝试抢占CPU资源,或者已经抢占到CPU资源正在运行,这俩种情况的状态都显示为RUNNABLE
BLOCKED锁阻塞线程A和线程B都要执行方法test,而且方法test被加了锁,线程A先拿到了锁去执行test方法,线程B这时候需要等待线程A把锁释放。这时候线程B就是处理BLOCKED
WAITING无限期等待一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TIMED_WAITING有限期等待和WAITING状态类似,但是有一个时间期限,时间到了,自己也会主动醒来
TERMINATED终止(死亡)run方法执行结束的线程处于这种状态。

注意,其实 BLOCKED,WAITING,TIMED_WAITING 这三种都属于线程阻塞,只是触发的条件不同,以及从阻塞状态中恢复过来的条件也不同而已。

线程在这三种情况的阻塞下,都具备相同的特点:

  • 线程不执行代码
  • 线程也不参与CPU时间片的争夺

线程状态变化的情况如下:

在这里插入图片描述

一个线程从创建到启动、到运行、到死亡,以及期间可能出现的情况都在上图中进行了描述。

在后面的学习中,会陆续认识到这里面的每一种情况!

一个线程,经历的最普通的过程如下:

public class Test {

    public static void main(String[] args) {


        Thread t1 = new Thread("t1线程"){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {

                }
            }
        };

        System.out.println(t1.getState());
		
        //启动t1线程
        t1.start();

        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());
        System.out.println(t1.getState());


    }

}
//运行结果: 注意需要多运行几次,因为可能每次运行的情况不一样
NEW
RUNNABLE
RUNNABLE
RUNNABLE
RUNNABLE
RUNNABLE
RUNNABLE
TERMINATED
TERMINATED

注意1,刚创建好的线程对象,就是出于NEW的状态

注意2,线程启动后,会出于RUNNABLE状态

注意3,其实这个RUNNABLE状态包含俩种情况:

  • 就绪状态,此时这个线程没有运行,因为没有抢到CPU的执行权
  • 运行状态,此时这个线程正在运行中,因为抢到CPU的执行权

注意4,JavaAPI中并没有定义就绪状态和运行状态,而是把这俩情况统一叫做RUNNABLE(可运行状态),但是一般我们为了能更加清楚的描述问题,会用上就绪状态和运行状态

注意5,在线程多次抢到CPU执行权,“断断续续”把run方法执行完之后,就变成了TERMINATED状态(死亡),之所以是“断断续续”的运行,是因为每次抢到CPU执行权的时候,只是运行很小的一个时间片,完了之后还要重新抢夺下一个时间片,并且中间还有可能抢不到的情况

注意6,死亡后的线程,不能重新启动

和上面描述对应的状态图如下:

在这里插入图片描述

从就绪状态到运行状态,之间会经过多次反复的CPU执行权的争夺(线程调度)

这就是一个线程经历的最基本的状态变化。

其他的状态都是线程在Running的时候,线程中调用了某些方法,或者触发了某些条件,导致这个线程进入到了阻塞状态(上面介绍的三种阻塞情况)

燧原科技云燧ESL64-O超节点:国产AI芯片与OEX光互联架构解析 分布式AI训练通过将计算任务拆分到多个节点并行执行来加速模型训练,其核心原理涉及数据并行和模型并行等策略。在技术价值层面,分布式训练能显著提升大规模深度学习任务的效率,但传统GPU集群常因网络通信瓶颈导致性能受限。OEX光互联架构作为解决通信瓶颈的关键创新,采用硅光技术实现高带宽、低延迟的节点内互联,优化了AI训练中的集合操作。这种硬件级优化特别适用于千亿参数大模型训练和科学计算等场景,能有效降低AI集群的总体拥有成本(TCO)。云燧ESL64-O超节点正是基于国产自研AI芯片与OEX架构的集成方案,为大规 阅读详情

相关推荐

MRI脑影像分析从哲学到技术:一文搞懂VBM预处理基本原理(全网最详细解析)

   分析大脑解剖学差异最常用方法之一是基于体素的形态学方法(voxel-based morphometry, VBM)。 VBM的基本思想是测量大脑组织,尤其是灰质的局部浓度的差异。在大脑的每个体素处进行操作,然后在两个或多个实验组之间进行统计比较,从而在受调查的组之间建立特定大脑区域中脑组织浓度的统计学显着差异。 VBM分析基于(高分辨率)MRI脑部扫描,涉及大量处理步骤,主要是空间归一化,分割,平滑和统计分析,最终结果是统计图,显示了各组之间组织类型明显不同的区域[^1]。 参考 [^1] ..

意疏的学习笔记 3万+

JAVA进程和线程

进程就是正在运行的程序,它是系统资源调度的独立单位,并且一个进程可以执行多个任务,而线程就是程序执行的任务,它是程序使用CPU的基本单位,因此也可以说线程是依赖于进程的。像比如任务管理器里面的运行任务就是线程。

程序猿追的博客 9601

计算文本相似度的常用算法

NLP、数据挖掘领域中,文本分析是一个很重要的领域,这有助于我们去让计算机理解语言的作用和使用。文本分析是数据挖掘的重要手段,利用文本分析,我们将很快的读取到一本书、一篇文章、一段话中的关键词和核心思想,而文本相似度就是我们用来剔除无用信息或者重复信息的重要手段,让计算机去找文本中的不同。 在生活中,信息检索、数据挖掘、机器翻译、文档复制检测等领域都应用到“文本相似度”。文本不仅仅是文字,文本相似度的应用更广,除了文字的匹配,还可以是图片、音频等,因为它们的实质都是在计算机中都是以二进制的方式存在的。相.

氧小氢的博客 2万+

Java 进程与线程

进程与线程区别;以及线程的实现方式和简单的安全方式

等,一起的博客 693

用Keil5给CH552G编程:手把手配置8051开发环境+LED闪烁实战

本文详细介绍了如何使用Keil5为CH552G单片机配置8051开发环境,并完成LED闪烁实战。从获取必要软件、安装WCH器件库到创建工程、编写程序及烧录,手把手指导开发者避开常见陷阱,特别适合从STC转向CH552G的开发者。

weixin_30733003的博客 537

SPAD 单光子雪崩二极管

SPAD,全称为,是一种高灵敏度的光电探测器件,能够在非常低的光照水平下检测单个光子。SPAD在工作时,当一个光子被探测器吸收后,能够触发一个雪崩效应,产生一个可以被检测到的电信号。

qq_42786033的博客 1万+

Java基础——线程(进程和线程,线程的开启方式、相关方法、安全和同步)

1) 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动是系统进行资源分配的基本单位(简单解释为程序的执行过程)2)每一个进程都有自己的空间,在没有经过进程本身允许的情况下,一个进程不可以直接访问其它的的进程空间进程是动态产生,动态消亡的任何进程都可以同其它进程一起并发执行3) 多进程同时工作,对于CPU而言,它是在多个进程间轮换执行的。

qq_38553011的博客 1497

基于TI MSPM0的PS2双轴按键摇杆模块ADC+GPIO驱动移植实战

本文详细介绍了在TI MSPM0微控制器上驱动PS2双轴按键摇杆模块的完整实战流程。通过SysConfig图形化工具配置ADC与GPIO引脚,并编写了包含ADC采样、按键检测及数据处理的驱动代码,实现了对摇杆X/Y轴模拟量及按键数字量的稳定读取,为嵌入式控制项目提供了可直接移植的解决方案。

weixin_42465140的博客 83

多线程基础(1):线程和进程,创建线程的五种方式

一.关于进程和线程 1.什么是进程 进程在操作系统中是这样进行管理的: 描述:PCB,叫做进程管理块,在创建进程时,建立 PCB,伴随进程运行的全过程,直到进程撤销而撤销. (包含 PID,内存指针,文件描述符表,进程的状态,上下文,优先级,记账信息…)实际上这个 PCB 是一个非常大的结构体,属性非常多 组织:通过双向链表进行组织 虚拟地址空间:操作系统会直接依据真实的内存地址进行划分空间,分配给每一个进程. 2.多线程的概念 一个线程就是一个"执行流".每个线程之间都可以按照顺序执行自己的代

m0_51928077的博客 1902

Ubuntu 22.04源码编译安装ROS Noetic

Ubuntu 22.04源码编译安装ROS Noetic

Drknown的博客 2万+

【C语言趣味教程】(0) 专栏介绍

这是一套C 语言趣味教学专栏,目前正在火热连载中,欢迎猛戳订阅!本专栏保证篇篇精品,继续保持本人一贯的幽默式写作风格,当然,在有趣的同时也同样会保证文章的质量,旨在能够产出"有趣的干货" !本系列教程不管是零基础还是有基础的读者都可以阅读,可以先看看目录!标题前带星号 (*) 的部分不建议初学者阅读,因为内容难免会超出当前章节的知识点,面向的是对 C 语言有一定基础或已经学过

有道无术, 术尚可求。有术无道, 止于术。 2万+

多线程编程(上):进程.线程概述&创建线程

本章概述 Java 中的多线程机制可以让一个程序中的多个程序段同时运行,同时运行的每一个程序段就是一个线程,这样多个同时运行的程序段既相互独立运行,又紧密相关。编程过程中采用多线程机制,可以使系统资源利用率更高,在一些情况下可以使程序设计更简单,程序中有耗时的程序段时,使用多线程可以使程序运行更加流畅。 一. 进程.线程概述 1.1进程和线程基本概念 下面是程序,进程和线程这几个概念的区别和联系 程序:是一段静态的代码,是人们解决问题的思维方式在计算机中的描述,是应用软件执行...

Lucky_mzc的博客 1658

初探802.11协议(6)——Wi-Fi 6新特性简介

Wi-Fi 6新机制(特性)介绍

FanFF的博客 1522

零基础掌握Java多线程(一):进程与线程、线程的创建

通过重写run()方法实现线程的具体逻辑。run()方法是线程的执行体,是线程的核心。定义了线程启动后要执行的任务内容。可以通过继承Thread类并重写run()方法,或实现Runnable接口的run()方法来实现。Runnable接口是重写run(),Callable接口是重写call()。

DNA314的博客 1017

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Java线程与进程基础——以及四种创建方法

进程与线程 进程 简单理解为一次程序一次执行(包括代码以及内存数据等等) 线程 程序中单个顺序流称为控制成为进程(例如一个程序要边下载,边计算,边观看.....都可以称为一个线程) 线程特点 一个进程有多个线程 多个线程分享cpu(并发的或以时间片的方式进行) 共享内存(如多个线程访问同一对象) window系统查看进程与线程 创建线程的四种方法(创建Run方法) 继承Thread方法(最常用) package ThreadLearn; /**首先要继承.

qq_58510930的博客 406

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Java多线程基础(线程与进程的区别,线程的创建方式及常用api,线程的状态)

多线程几乎是面试必考内容,基础也尤为重要,所以对多线程基础进行了总结,包括线程与进程的区别和联系,线程的常用api,线程的创建方式,线程的状态

X_H学Java的博客 1323

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java基础:初识线程,进程,了解主线程与创建多线程的两种方式

线程相关概念 并行与并发 概念阶段: 并行:指两个或多个事件在同一时刻发生。同时执行 并发:指两个或多个事件在同一时间段内发生。交替执行 进程与线程 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程; 进程是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。 线程:线程是指进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序可以称之为多线

JinxLin的博客 634

java线程创建与cpu使用吗_零基础学Java之JavaSE,第七课多线程,进程线程和线程的定义创建...

小伙伴们,我们分享继续哦第一节进程和线程https://www.bilibili.com/video/BV137411V7Y1/?p=1951.1 进程和线程程序Program是一段静态的代码,它是应用程序执行的蓝本进程Process是指一种正在运行的程序,有自己的地址空间进程的特点动态性并发性独立性并发和并行的区别并行:多个CPU同时执行多个任务并发:一个CPU(采用时间片)同时执行多个任务生活...

weixin_35377028的博客 104

Java基础(十八)——线程与进程、多线程(本质、创建方式)、线程的休眠、生命周期、临界资源

Java基础(十八)——线程与进程 一、初识线程与进程 打开任务管理器,即可看到进程 进程:程序执行的j基本单位,程序由一个或者多个进程组成。 线程:线程是 CPU 执行的基本单位,进程是有一个或者多个线程组合合成。 举个例子: 音乐播放器播放音乐的同时还有歌词在滚动。其中,歌词就是一个线程在负责,播放音乐又是另外一个线程在负责。 二、初识多线程 1、多线程的本质 线程是由 CPU 调度执行,多线程是 CPU 不断地来回切换调度不同的线程执行的结果。 多线程创建的方式: ...

qq_41824825的博客 838

java基础(19)(线程与进程,创建线程的方式,线程的状态,火车卖票问题,同步机制,死锁)

线程 并发与并行 并发:指两个或多个事件在同一时间段内发生 并行:指两个或多个事件在同一时刻发生 线程与进程 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。 一个程序运行过至

qq_41934990的博客 361

Java线程

Java线程 文章目录Java线程线程基础什么是线程为什么使用线程线程与进程联系区别如何实现线程继承Thread类继承Runnable接口继承Callable接口创建线程三种方法的区别线程的生命周期线程创建线程就绪线程运行线程阻塞等待阻塞同步阻塞规避线程风险 线程基础 什么是线程 线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中

weixin_44774675的博客 639

Java多线程基础

Java多线程基础

qq_47799626的博客 883

简单了解在Java中的线程,以及如何创建并使用

简单了解在Java中的线程,以及如何创建并使用。其包括线程与进程的关系、在Java的Thread类中start与run方法的区别、在Java中线程的6种状态和转换方式、在Java中创建线程并使用的方式。

都随它大小便吧 4247

Java线程知识及线程创建

1. Java线程   讲到线程,首先需要了解进程,再来了解什么是线程。在博客线程安全中提到线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、文件I/O等),又可以独立调度(线程是CPU调度的基本单位)。   Java语言提供了在不同硬件和操作系统平台下对线程操作的统一处理,每一个已经执行start()且还未结束的java...

zz1x2c3的博客 200

Java进程线程

Java进程线程 1.进程与线程 1.1 引入 现代操作系统(Windows,macOS,Linux)都可以执行多任务。多任务就是同时运行多个任务。 CPU执行代码都是一条一条顺序执行的,但是,即使是单核cpu,也可以同时运行多个任务。因为操作系统执行多任务实际上就是让CPU对多个任务轮流交替执行。 例如,假设我们有语文、数学、英语3门作业要做,每个作业需要30分钟。我们把这3门作业看成是3个任务,可以做1分钟语文作业,再做1分钟数学作业,再做1分钟英语作业: 这样轮流做下去,在某些人眼里看来,做作业的速度

weixin_46503355的博客 265

java线程(一):线程创建与线程状态

关于线程,线程是java中非常重要的一部分。很多程序的设计离开不了对线程的设计。多任务本质上就是实现多线程。多线程是实现多任务的一种。说到线程必须要提到进程,进程指的是在内存中运行的应用程序,每个进程都有自己的运行内存空间,一个进程可以启动多个线程。java.lang.Thread类实例中每个线程都有一个调用栈。      java程序总是从main()方法开始执行,main方法存放在一个线程内,成

Richard_Cai 1232
上一篇: Java基础(异常)
下一篇: Java基础(线程方法,线程同步,死锁)
平平无奇的编码小天才
博客等级 码龄6年 15粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值