Java基础多线程

本文详细介绍了Java中的进程与线程概念,包括进程与线程的关系、多线程的使用原因及应用场景。讨论了多线程的实现方式,如继承Thread、实现Runnable和Callable接口,并对比了各种方式的优缺点。此外,还探讨了线程的状态、线程池的四种类型,以及线程控制方法如start()、run()、interrupt()、wait()、notify()等,并分析了它们之间的区别和作用。
摘要由CSDN通过智能技术生成

进程与线程

1、进程
进程是资源(CPU,内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
2、线程
线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

一个正在运行的软件(如迅雷)就是一个进程,一个进程可以同时运行多个任务(迅雷软件可以同时下载多个文件,每个下载任务就是一个线程),可以简单的认为进程是线程的集合。

线程是一条可执行的路径。
对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。
对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的。

3、进程与线程的关系
一个程序就是一个进程,而一个程序中的多个任务则被称为线程。进程是表示资源分配的基本单位,又是调度运行的基本单位。亦即执行处理机调度的基本单位。进程和线程的关系:
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。即每个线程都有自己的堆栈和局部变量。
处理机分给线程,即真正在处理机上运行的是线程。
线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

如果把上课的过程比作进程,把老师比作CPU,那么可以把每个学生比作每个线程,所有学生共享这个教室(也就是所有线程共享进程的资源),上课时学生A向老师提出问题,老师A进行解答,此时可能会有学生B对老师的解答不懂会提出B的疑问(注意:此时可能老师还没有对A同学的问题解答完毕),此时老师又向学生B解惑,解释完之后又继续回答学生A的问题,同一时刻老师只能向一个学生回答问题(即:当多个线程在运行时,同一个CPU在某一时刻只能服务于一个线程,可能一个线程分配一点时间,时间到了就轮到其他线程执行了,这样多个线程在来回的切换)

4、为什么要使用多线程
多线程可以提高程序的效率。

5、多线程应用场景
一般线程之间比较独立,互不影响
一个线程发生问题,一般不影响其它线程

多线程的实现方式

1、顺序编程
程序从上往下的同步执行,即如果第一行代码执行没有结束,第二行代码就只能等待第一行结束后才能结束。

public class Main {
   
    // 顺序编程 吃喝示例:当吃饭吃不完的时候,是不能喝酒的,只能吃完晚才能喝酒
    public static void main(String[] args) throws Exception {
   
		// 先吃饭再喝酒
        eat();
        drink();
    }

    private static void eat() throws Exception {
   
        System.out.println("开始吃饭?...\t" + new Date());
        Thread.sleep(5000);
        System.out.println("结束吃饭?...\t" + new Date());
    }

    private static void drink() throws Exception {
   
        System.out.println("开始喝酒?️...\t" + new Date());
        Thread.sleep(5000);
        System.out.println("结束喝酒?...\t" + new Date());
    }
}

在这里插入图片描述
2、并发编程
多个任务可以同时做,常用与任务之间比较独立,互不影响。
线程上下文切换:
同一个时刻一个CPU只能做一件事情,即同一时刻只能一个线程中的部分代码,假如有两个线程,Thread-0和Thread-1,刚开始CPU说Thread-0你先执行,给你3毫秒时间,Thread-0执行了3毫秒时间,但是没有执行完,此时CPU会暂停Thread-0执行并记录Thread-0执行到哪行代码了,当时的变量的值是多少,然后CPU说Thread-1你可以执行了,给你2毫秒的时间,Thread-1执行了2毫秒也没执行完,此时CPU会暂停Thread-1执行并记录Thread-1执行到哪行代码了,当时的变量的值是多少,此时CPU又说Thread-0又该你,这次我给你5毫秒时间,去执行吧,此时CPU就找出上次Thread-0线程执行到哪行代码了,当时的变量值是多少,然后接着上次继续执行,结果用了2毫秒就Thread-0就执行完了,就终止了,然后CPU说Thread-1又轮到你,这次给你4毫秒,同样CPU也会先找出上次Thread-1线程执行到哪行代码了,当时的变量值是多少,然后接着上次继续开始执行,结果Thread-1在4毫秒内也执行结束了,Thread-1也结束了终止了。CPU在来回改变线程的执行机会称之为线程上下文切换。

public class Main {
   
    public static void main(String[] args) {
   
	    // 一边吃饭一边喝酒
        new EatThread().start();
        new DrinkThread().start();
    }
}

class EatThread extends Thread{
   
    @Override
    public void run() {
   
        System.out.println("开始吃饭?...\t" + new Date());
        try {
   
            Thread.sleep(5000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        System.out.println("结束吃饭?...\t" + new Date());
    }
}

class DrinkThread extends Thread {
   
    @Override
    public void run() {
   
        System.out.println("开始喝酒?️...\t" + new Date());
        try {
   
            Thread.sleep(5000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        System.out.println("结束喝酒?...\t" + new Date());
    }
}

并发编程,一边吃饭一边喝酒总共用时5秒,比顺序编程更快,因为并发编程可以同时运行,而不必等前面的代码运行完之后才允许后面的代码。
在这里插入图片描述
本示例主要启动3个线程,一个主线程main thread、一个吃饭线程(Thread-0)和一个喝酒线程(Thread-1),共三个线程, 三个线程并发切换着执行。main线程很快执行完,吃饭线程和喝酒线程会继续执行,直到所有线程(非守护线程)执行完毕,整个程序才会结束,main线程结束并不意味着整个程序结束。
在这里插入图片描述
顺序:代码从上而下按照固定的顺序执行,只有上一件事执行完毕,才能执行下一件事。就像物理电路中的串行,假如有十件事情,一个人来完成,这个人必须先做第一件事情,然后再做第二件事情,最后做第十件事情,按照顺序做。
并行:多个操作同时执行,他们之间是并行的。假如十件事情,两个人来完成,每个人在某个时间点各自做自己的事情,互不影响。
并发:将一个操作分割成多个部分执行并允许无序处理,假如有十件事情,如果有一个人在做,这个人可能做一会这个就不想做了,再去做别的,做着做着可能也不想做了,又去干其他事情了,看他心情想干哪个就干哪个,最终把十件事情都做完。如果有两个人再做,他们俩先分一下,比如张三做4件,李四做6件,他们各做自己的,在做自己的事情过程中可以随意的切换到别的事情,不一定要把某件事情干完再去干其他事情,有可能一件事做了N次才做完。

通常一台电脑只有一个CPU,多个线程属于并发执行,如果有多个CPU,多线程并发执行有可能变为并行执行。
在这里插入图片描述

多线程的创建方式

继承Thread
实现Runable
实现Callable
继承java.lang.Thread,重写run()方法

public class Main {
   
    public static void main(String[] args) {
   
        new MyThread().start();
    }
}

class MyThread extends Thread {
   
    @Override
    public void run() {
   
        System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());
    }
}

Thread类

package java.lang;
public class Thread implements Runnable {
   
	// 构造方法
	public Thread(Runnable target);
	public Thread(Runnable target, String name);
	
	public synchronized void start();
}

实现java.lang.Runnable接口,重写run()方法,然后使用Thread类来包装
Runnable接口

package java.lang
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值