JavaEE——多线程1(超详细版)
目录
基础理论知识:
进程(process):
1.如何管理进程:
2.PCB(进程控制块):
线程(Thread):
一.创建线程
1.继承thread类
2.实现Runnable接口
3.匿名内部类创建Thread子类对象
4.匿名内部类创建Runnable子类对象
5.使用lambda表达式创建Runnable子类对象
二.Thread常用的构造方法
三.Thread的几个常见属性
四.启动一个线程--start()
五.中断一个进程(两种方式)
1.通过共享标志位进行沟通通知
2.调用Interrupt()方法来通知
六.等待一个进程--join()
七.获取当前线程引用
八.休眠当前线程--sleep()
线程的状态
基础理论知识:
1.冯.诺依曼体系结构
cpu(中央处理器)(可用各种算术运算,逻辑判断的“通用计算机芯片”),存储器(硬盘 ,内存...),输入设备(键盘,鼠标...),输出设备(显示器...)
硬盘和内存对比:
硬盘:读写速度慢,存储空间大,成本低,断电不丢失数据
内存:读写速度快,存储空间小,成本高,断电丢失数据
2.指令:指导CPU进行工作的命令
一条指令(程序)执行过程:
a.读取指令
b.解析指令
c.执行指令
3.寄存器:在CPU上存储数据的模块(CPU存储数据实际是通过寄存器)(在CPU上执行指令,进行各种运算存储临时数据)
CPU的寄存器和内存的对比:
CPU寄存器·:读写速度快,存储空间小,成本高,断电丢失数据
内存:读写速度慢,存储空间大,成本低,断电丢失数据
进程(process):
一个运行起来的程序
1.如何管理进程:
使用结构体,进程控制块(PCB),将多个进程组织起来
2.PCB(进程控制块):
a.pid(进程id):进程标识符
b.内存指针:进程需要知道需要执行的指令在哪,指令依赖的数据又在哪
我们可以得出:进程运行过程中,需要依赖内存资源
c.文件描述符表:进程运行,依赖到网盘 ,网卡等相关资源;进程运行,执行指令,依赖CPU,也消耗CPU资源
我们又可以得出结论:进程是操作系统资源分配的基本单位
以下也是进程调度
d.进程状态(这里只提到了两个)
--》就绪状态:随叫随到
--》阻塞状态:进程当前不适合在CPU上执行
e.进程优先级:有些进程,优先级更高,可以得到的CPU资源更多
f.进程上下文:保存上下文 恢复上下文
g.进程的记账信息
线程(Thread):
可以说是轻量级的进程,一个线程就是一个“执行流”
1.为什么要用线程
a.“并发编程”成为刚需
b.线程比进程更轻量:创建线程比创建进程更快,销毁线程比销毁进程更快,调度线程比调度进程更快
!!!2.进程和线程的区别!!!
A.进程是包含线程的(每个进程至少含有一个线程,即主线程)
B.进程与进程之间是不共享资源空间的,同一个进程的多个线程之间是共享同一个资源空间的
C.进程是操作系统资源分配的最小单位,线程是操作系统调度的最小单位
D.一个进程挂了不会影响其他进程,同一个进程的一个线程挂了会一并带走其他线程(整个进程崩溃
)
一.创建线程
1.继承thread类
要子类重写父类run方法
package Thread;import static java.lang.Thread.sleep;public class demo {//第二个线程public static class mythread extends Thread{@Overridepublic void run() {//子类对父类方法的重写while(true){System.out.println("这是一个thread线程...");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}//主线程public static void main(String[] args) {mythread thread=new mythread();//向上转型thread.start();//真正创建了一个线程while (true){System.out.println("这是一个主线程...");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

2.实现Runnable接口
需要重写run方法
package Thread;import static java.lang.Thread.sleep;public class demo2 {public static class myrunnnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("这是一个runnable实现的线程");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}public static void main(String[] args) {//方式一Thread thread=new Thread(new myrunnnable());//方式二// Runnable runnable=new myrunnnable();//Thread thread=new Thread(runnable);thread.start();while (true){System.out.println("这是一个mian线程...");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3.匿名内部类创建Thread子类对象
package Thread;import static java.lang.Thread.sleep;public class dome3 {public static void main(String[] args) {Thread thread=new Thread() {@Overridepublic void run() {while (true) {System.out.println("这是匿名内部类创建thread子类对象实现的线程");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};thread.start();while (true) {System.out.println("这是mian实现的线程");try {thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

4.匿名内部类创建Runnable子类对象
package Thread;public class dome4{public static void main(String[] args) {Thread thread=new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("这是匿名内部类创建Runnable子类对象实现的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});thread.start();while (true) {System.out.println("这是mian实现的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}

5.使用lambda表达式创建Runnable子类对象
package Thread;public class dome5 {public static void main(String[] args) {//()->{}创建了一个匿名函数式接口的子类,并创建对应的实例,重写里面的方法Thread thread=new Thread(()->{ while(true) {System.out.println("函数式接口调用");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();while (true) {System.out.println("这是mian实现的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

二.Thread常用的构造方法
1.Thread()//创建线程对象
2.Thread(Runnable target)//使用Runnable接口创建子类对象
3.Thread(String name)//创建线程对象,并命名
4.Thread(Runnable target,String name)//使用Runnable接口创建子类对象,并命名
三.Thread的几个常见属性
ID 获取方法:getId()
名称 获取方法:getName()
状态 获取方法:getState()
优先级 获取方法:getPriority()
是否后台线程 获取方法:isDaemon()
什么是前台线程?什么是后台线程?
前台线程可以理解为 自己创建的线程和main主线程;可以用setDaemon方法去修改;前台线程有多个时,全部结束,线程结束
后台线程可以理解为 除了前台线程的线程,不影响线程的结束
JVM会在一个进程中的所有非后台线程结束后,才会结束运行
是否存活 获取方法:isAlive()
可以理解为run方法是否运行结束
是否中断 获取方法:isInterrupt()
四.启动一个线程--start()
是JVM提供的方法,本质是调用操作系统的API
调用start才是在操作系统底层中创建了一个线程
!!!一个Thread对象只能start一次!!!
五.中断一个进程(两种方式)
也可以理解为终止一个进程,就是让线程的入口方法尽快结束
1.通过共享标志位进行沟通通知
package Thread;public class dome6 {public static volatile boolean flag=false;public static void main(String[] args) {Thread thread=new Thread(()->{while(!flag) {System.out.println("这个线程正在进行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("不能运行了,累了...");flag=true;}
}

2.调用Interrupt()方法来通知
package Thread;public class demo7 {public static void main(String[] args) {Thread thread=new Thread(()->{while(!Thread.interrupted()) {System.out.println("这个线程正在进行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("不能运行了,累了...");Thread.interrupted();}}

思考:为什么代码又继续运行了呢?
是因为异常引起的,当我们调用sleep/wait/join方法,会以异常的方式通知,清除中断标志位,所以,取决于我们在处理异常的时候,关键在于catch代码块
如下代码:
package Thread;public class demo7 {public static void main(String[] args) {Thread thread=new Thread(()->{while(!Thread.currentThread().isInterrupted()) {//while(Thread.interrupted()){//两种方法都可以System.out.println("这个线程正在进行...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;//break; 立即结束//什么也不写 不终止//写其他逻辑再break 稍后终止}}});thread.start();try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("不能运行了,累了...");thread.interrupt();}
}

六.等待一个进程--join()
可以理解为多个线程,线程结束的先后顺序
package Thread;public class demo8 {public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("线程1在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("线程2在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread1.start();thread2.start();thread.join();thread.join();}
}

我们使用再次使用join方法的正确位置后再观察(先start再join)
package Thread;public class demo8 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("线程1在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("线程2在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread1.start();thread1.join();thread2.start();thread2.join();}
}

同时,join()在括号中也可以添加参数,避免死等的情况(即超时等待)
七.获取当前线程引用
public static Thread currentThread()返回当前线程对象的引用
八.休眠当前线程--sleep()
实际休眠时间大于等于设置参数的时间(由于CPU的调度)
它是Thread类中的方法,可以进行超时等待
线程的状态
1.NEW:安排了⼯作,还未开始行动(也可以理解为new一个对象,但是没有进行start操作)
2. RUNNABLE:可⼯作的,又可以分成正在⼯作中和即将开始⼯作(也可以理解为就绪状态 a.正在CPU上工作 b.随时准备到CPU上工作)
3. BLOCKED:这⼏个都表示排队等着其他事情(由于锁
导致的阻塞)
4. WAITING:这⼏个都表示排队等着其他事情(没有超时的等待(死等))
5. TIMED_WAITING:这⼏个都表示排队等着其他事情(指定时间的阻塞(线程阻塞不参与CPU的调度),超时等待)
6. TERMINATED:⼯作完成了(可以理解为线程结束了,但是thread对象还在)

!!!前面学到的isAlive()可以理解为不是new和terminated状态都是活着的!!!
多线程内容,先到这里,有问题欢迎评论区留言讨论,请同学们多多练习相关知识!!!看我后续内容
