Day29 多线程
一、什么是进程
进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。
二、单进程操作系统和多进程操作系统的区别
单进程操作系统:dos(一瞬间只能执行一个任务)
多进程单用户操作系统:Windows(一瞬间只能执行多个任务)
多进程多用户操作系统:Linux(一瞬间只能执行多个任务)
三、现在的多核CPU是否可以让系统在同一个时刻可以执行多个任务吗?
理论上是可以的
四、什么是线程,理解线程和进程的关系
什么是线程?
线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源
一个进程 可以有 多个线程:各个线程都有不同的分工
理解线程和进程的关系
进程 与 进程 之间的关系:进程之间的内存空间和系统资源是独立的
同一个进程里的多条线程 :线程之间的内存空间和系统资源是共享的
进程里:可以有一条或一条以上的线程
进程里只有一条线程的情况下,这条线程就叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
Ps:线程是在进程里的,他们是包含关系
五、我们应用的软件有哪些是多线程的应用?
都是
六、Java中,如何来编写多线程的应用程序?有哪些方法?
1.线程类
创建MyThread类,继承Thread,重写run方法
public class Test01 {
public static void main(String[] args) {
//创建线程的对象
MyThread t = new MyThread();
//启动线程
t.start();
}
}
//线程类
class MyThread extends Thread{
//当前线程抢到cpu资源后,就会执行run方法
@Override
public void run() {
System.out.println("当前线程抢到资源了");
}
}
2.任务类
创建Task类,实现Runnable接口中的run方法
public class Test01 {
public static void main(String[] args) {
Thread t = new Thread(new Task());
t.start();
}
}
//任务类
class Task implements Runnable{
//当前线程抢到cpu资源后,就会执行run方法
@Override
public void run() {
System.out.println("抢到资源了");
}
}
3.带返回值的任务类
4.线程池
七、感受多线程之间争抢资源的场景
需求:编写一个多线程的应用程序,主线程打印1-100之间的数字,子线程打印200-300之间的数字,观察其输出的结果,体会多线程互相争抢资源的场景
public class Test01 { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); for (int i = 1; i <= 100; i++) { System.out.println("主线程:" + i); } } } public class MyThread extends Thread{ @Override public void run() { for (int i = 200; i <= 300; i++) { System.out.println("子线程:" + i); } } }
八、小结
进程 与 进程 的关系:独享内存空间和系统资源
线程 与 进程 的关系:有一个进程中至少包含一个线程
线程 与 线程 的关系:在同一个进程里,多个线程共享内存空间和系统资源
一个进程中包含多个线程,只有一个主线程
经典面试题:请问当我们编写一个单纯的main方法时,此时该程序是否为单线程的?为什么?
不是,因为java有个重要的特性,叫垃圾自动回收机制,所以答案是多线程,这里面有两部分,主线程(用户线程),垃圾回收线程GC(守护线程)同时存在。
垃圾回收器是一个后台线程。
九、线程的优先级别
含义:给线程定义抢到CPU资源的优先级
理解:
1.优先级别:1~10,数字越大,优先级越高
2.线程的优先级别不能决定线程是否优先抢到资源,优先级别只能影响(概率问题)
需求:在主线程中创3个子线程,并且设置不同优先级,观察其优先级对线程执行结果的”影响”。
a.setPriority(Thread.MAX_PRIORITY);//10 b.setPriority(Thread.NORM_PRIORITY);//5 c.setPriority(Thread.MIN_PRIORITY);//1
十、给线程自定义名称
- 自己写getThreadName/setThreadName
- 调用父类的一个参数的构造方法和getName
- Thread.currentThread().getName()
十一、让线程休眠
需求:编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名
Thread.sleep(1000);
此方法为静态方法,写在哪个线程中,哪个线程就休眠
十二、线程的礼让
需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次,观察实验结果
Thread.yield();
此方法为静态方法,此方法写在哪个线程中,哪个线程就礼让
所谓的礼让是指当前线程退出CPU资源,并转到就绪状态,接着再抢
十三、线程的合并
需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程
t.join(); 合并方法,t.join() 会阻塞主线程,直到子线程 t 执行完毕
十四、线程的中断
1、含义: 在 Java 中,线程的中断是一种用于通知线程停止执行的机制。线程的中断由其他线程通过调用
interrupt()
方法来触发。当一个线程被中断时,它会收到一个中断请求,可以根据具体的业务逻辑来决定如何处理这个中断请求。2、中断机制:
- 中断标识:每个线程都有一个中断标识,用于标识线程是否被中断。可以通过
isInterrupted()
方法来查询线程的中断状态,或者通过interrupted()
方法来查询当前线程的中断状态并清除中断标识。- 中断处理:线程可以通过调用
interrupt()
方法来请求中断自己或其他线程。被中断的线程会收到一个中断请求,可以根据具体的业务逻辑来决定如何处理这个中断请求。- 中断状态:当线程处于阻塞状态(如调用
sleep
、wait
、join
等方法时)时,如果收到中断请求,会抛出InterruptedException
异常,可以在异常处理中进行相应的处理。- 清除中断标识:在处理中断请求后,可以通过
Thread.interrupted()
方法来清除当前线程的中断状态,以便后续的操作不受影响。
十五、守护线程
守护线程 默默守护着前台线程,当所有的前台线程都消亡后,守护线程会自动消亡
注意:垃圾回收器就是守护线程
t.setDaemon(true);
十六、线程局部变量(实现线程范围内的共享变量)
1、含义: 线程局部变量(Thread Local Variables)是一种 Java 中用于实现线程范围内的共享变量的机制。它允许每个线程都拥有自己独立的变量副本,从而在多线程环境下实现共享变量的线程封闭性。
2、应用场景: 线程局部变量通常用于需要在线程范围内共享变量,但又需要保持线程封闭性的场景,例如线程池中的任务、Web 应用中的用户会话信息等。通过线程局部变量,可以更加安全地在多线程环境下共享变量。
十七、线程的生命周期
1、新建状态
i. 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。
ii. 例如:Thread thread=new Thread();
2、 就绪状态
i. 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。
3、运行状态
i. 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。
4、 阻塞状态
i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
5、死亡状态
i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。