多线程
一、什么是进程
进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。
二、单进程操作系统和多进程操作系统的区别
单进程操作系统 dos:一次只能执行单个任务
多进程操作系统 Windows:一次可以执行多个任务
三、系统在同一个时刻可以执行多个任务吗?
理论上,同一个时刻可以执行多个任务。
因为CPU切换比较快,有的进程正在运行,有的进程正在争抢资源,有的进程正在退出资源
四、什么是线程,理解线程和进程的关系
什么是线程?
线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源
一个进程里有多线程:各个线程都有不同的分工
理解线程和进程的关系?
进程:进程之间的内存空间和系统资源是独立的
线程:在同一个进程里的线程之间是共享内存空间和系统资源的
进程里:可以有一条或一条以上的线程
注意:
进程里只有一条线程的情况下,这条线程就叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
线程是在进程里的,他们是包裹关系
五、我们应用的软件有哪些是多线程的应用?
几乎都是
六、Java中,如何来编写多线程的应用程序?有哪些方法?
- 线程类
创建步骤:创建MyThread类,继承Thread,重写run方法
使用步骤:创建MyThread类的对象,调用start方法
- 任务类
创建步骤:创建Task类,实现Runnable接口,重写run方法
使用步骤:创建Thread类的对象,并把任务类的对象传入,调用start方法
带返回值的任务类
线程池
七、体会多线程争抢资源的场景
需求:编写一个多线程的应用程序,主线程打印1-100之间的数字,子线程打印200-300之间的数字,观察其输出的结果,体会多线程互相争抢资源的场景
八、小结
线程类:继承Thread,重写run方法,创建线程对象,调用start方法(并非马上调用run方法)
任务类:实现Runnable接口,重写run方法,创建任务类对象,通过new Thread(task).start()
主线路从上往下,多线程多条执行路线(随机)
经典面试题:请问当我们编写一个单纯的main方法时,此时该程序是否为单线程的?为什么?
Java的垃圾回收器是一个后台线程,默默在后台销毁多余的对象
九、线程的优先级
需求:在主线程中创建三个的子线程,并且设置不同优先级,观察其优先级对线程执行结果的影响。
注意:
- 优先级别10-1,10是最高级别,1是最低级别
- 设置优先级别只能影响结果,不能决定结果
package com.dream.thread04;
public class Test01 {
public static void main(String[] args) {
/**
* 线程的优先级别
*/
//创建线程对象
A a = new A();
B b = new B();
C c = new C();
//设置优先级别
a.setPriority(Thread.MAX_PRIORITY);//最高级别:10
b.setPriority(Thread.NORM_PRIORITY);//默认级别:5
c.setPriority(Thread.MIN_PRIORITY);//最低级别:1
//启动线程
a.start();
b.start();
c.start();
}
}
十、给线程自定义名称
package com.dream.thread05;
public class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//Thread.currentThread():获取当前线程对象
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
十一、线程的休眠
Thread.sleep(1000);休眠当前线程,该方法在哪个线程中调用,就休眠哪个线程
需求: 编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名
package com.dream.thread06;
import java.util.Random;
public class Test01 {
public static void main(String[] args) throws InterruptedException {
/**
* 线程的休眠
* 需求: 编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名
*/
Random ran = new Random();
String[] names = {"麻生希","曹桂华","椎名空","北岛玲","水菜丽"};
String name = names[ran.nextInt(names.length)];
for (int i = 3; i > 0; i--) {
System.out.println(i);
Thread.sleep(1000);
}
System.out.println(name);
}
}
十二、线程的礼让
Thread.yield(); 礼让,写在哪个线程里,哪个线程就退出CPU资源,马上进入到争抢资源的状态
需求:创建两个线程A,B,分别各打印100次,从1开始每次增加1,其中B一个线程,每打印一次,就礼让一次,观察实验结果
十三、线程的合并
需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程
十四、线程的中断
方式1:自己编写逻辑使得线程中断
方式2:
Thread.currentThread().isInterrupted() – 判断当前线程是否消亡
t.interrupt(); – 改变当前线程状态
十五、守护线程
含义:守护线程又叫做后台线程,默默守护着前台线程,所有的前台线程消亡后,守护线程自动消亡
注意:垃圾回收器就是一个守护线程
十六、线程局部变量(实现线程范围内的共享变量)
1.自定义一个静态变量用于共享
//用静态变量 concurrenthashMap 作为线程间共享的变量,存储单个值
//其他类可以通过类名直接获取,达到共享,通过谁调用就获取到当前调用线程的对象来存放值。
//线程类通过map,将自己作为key,i作为值放入map
//普通类被线程类的run方法调用,执行成员方法时可以获取到当前类对象,进而通过map,直接获取到值。并输出打印
2.ThreadLocal
低层也是用一个map 来存放数据,通过get()、set()方法获取或者设置变量,内部是通过Thread.concurrentThread()方法获取当前线程对象,将当前线程对象作为key,传入值作为value;
获取时也是获取调用线程所对应的key和value
十七、线程的生命周期
a) 新建状态
i. 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。
ii. 例如:Thread thread=new Thread();
b) 就绪状态
i. 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。
c) 运行状态
i. 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。
d) 阻塞状态
i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
e) 死亡状态
i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。
线程的生命周期
Thread.currentThread().isInterrupted();
//判断当前线程是否被中断,true-中断 false-未中断
//通过Thread类的一个boolean 属性来判断当前线程 而不是真正意义上的 中断
//
Thread.currentThread().interrupt();
修改当前线程的中断状态;