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

本文详细介绍了Java中的进程与线程概念,包括进程的定义、线程的创建、并发与并行的区别、时间片调度、线程的状态以及线程的分类。通过实例展示了如何在Java中创建和启动线程,以及如何利用Runnable接口指定线程任务。文中还讨论了线程的优先级、线程组和线程状态的变化,帮助理解Java多线程的运行机制。
摘要由CSDN通过智能技术生成

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() {
   

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值