前言
本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!
本篇文章将带大家了解java编程中的重点内容,线程
,在java中与多进程编程相比,多线程编程对java来说更友好。
本篇文章也需要大家先对进程有一定的了解,废话不多,开车了。
贴心链接:🥰🥰🥰进程篇
一、认识线程
进程篇,已经带大家了解过进程了。
🙌🙌进程就是一个跑起来的程序,操作系统会为这个进程创造一个PCB类型
的结点,并把这个结点添加到 组织进程的数据结构
中去。
🥳🥳而进程可以通过并行与并发,从而实现多个进程同时运行的效果。
😎😎😎线程可以看作是更加轻量级的进程。
进程
可以看作是工厂
,
线程
可以看作是工厂里的流水线
,
线程是更轻量级的进程,那么这一点体现在了哪里呢??🤔🤔🤔
重量级的进程
🤠🤠🤠进程的一个重要概念:
进程是操作系统资源分配的基本单位。
进程的创建,调度,与销毁
的成本其实都挺高的。
拿进程的创建来举例。
创建一个进程之所以成本高的主要是因为资源分配
。
资源分配往往都是比较耗时的操作。
进程需要系统为自己分配一块内存。
系统去分配这一块内存,就需要去遍历自己的空闲内存的表(是一种数据结构),找到一块大小匹配的空间,然后再进行分配。
😐😐😐所以此时相对来说,进程就是重量级的。
所以频繁的创建,调度,与销毁进程,成本是相当之高的。
因此,聪明的👨💻程序猿就引入了一个新的概念,线程。
轻量级的线程
🤩🤩🤩引入线程的意义:
1️⃣单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源(即并发编程)。
2️⃣ 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程。
3️⃣ 创建线程比创建进程更快。
4️⃣ 销毁线程比销毁进程更快。
5️⃣ 调度线程比调度进程更快。
🤠🤠🤠线程的一个重要概念:
线程是操作系统调度执行的基本单位。
即进程篇提到的调度,本质上是去调度线程
。
进程之所以重量级就在于其资源分配。
所以线程就要规避掉这个缺点。
线程的特点
1️⃣线程被包含在进程中。(一个进程可以包含一个或多个
线程)
2️⃣一个线程就是一个 “执行流”, 每个线程之间都可以按照顺序执行自己的代码.,多个线程之间 “同时” 执行着多份代码。
3️⃣进程里的所有线程,共用系统分配给进程的这一份资源。
所以进程启动,创建第一个线程时
,需要耗费成本去向系统申请资源,在这个进程中创建其他的线程时,就不需要在向系统申请资源
,从而减少创建成本与资源开销。
所以进程的创建与销毁的效率就提高了许多。
总结
进程就好像一个工厂,
线程就好像工厂里的流水线,
新建一个工厂与新建一个流水线,一定是新建工厂开销更大。
可以通过新建线程,来共同完成任务。
但也不是线程越多越好。
(直接化身大老板🤑🤑🤑) 假设有一家餐馆,一共可以供20桌客人吃饭。
(过度压榨线程,效率低下😶🌫️😶🌫️😶🌫️) 如果只有1
个服务员(线程),可能会效率不够,忙不过来,此时就需要更多线程帮忙。
(没有充分发挥线程的作用,性能过剩😐😐😐)如果有20
个服务员(线程),那么就变成一对一服务了(且这还是客人坐满的情况下),显然是成本高了,效率也没高多少。
(不把资源当回事,就是壕🤩🤩🤩) 如果有40
个服务员(线程),那么服务员就会抢活干,抢不到活的服务员就只能闲着,这不就是白发工资吗 (资源浪费)。
而且如果一个线程崩了,会影响到甚至让其他线程也崩掉,进程没准也会挂。
- 20卓客人,要四五个服务员就够了,所以线程也如此,在数量在于合理,不在多。
进程与线程的区别:
1️⃣ 进程包含线程(进程中至少包含一个线程,即主线程),线程在进程里。
2️⃣ 每个进程都有自己独立的内存空间与文件描述符表,同一个进程中的多个线程,共享该进程的内存空间与文件描述符表。
3️⃣ 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位。
4️⃣ 进程之间具有独立性,一个进程出现bug不会影响到其他进程。
线程之间具有共享性,一个线程出现bug可能会影响到其他线程,导致这一个进程都被带走。
二、编写多线程程序
接下来会深刻的感受到以下两点
1️⃣每个线程都是一个独立的执行流
2️⃣多个线程之间是 “并发” 执行的
java标准库中提供了一个Thread类,来表示一个线程。
-
run方法,通过重写Thread类中的run方法,来对线程的执行代码进行编写。
run可以称为是线程的入口方法 -
start方法,执行这个方法,才是真正的在系统里创建了一个新线程,start方法会自动调用run方法。
继承 Thread 类
使用Thread类中的run方法来描述线程入口
class MyThread extends Thread {
//重写run方法,编写线程代码
@Override
public void run() {
while (true) {
System.out.println("hello thread");
}
}
}
public class threadDemo1 {
public static void main(String[] args) {
//创建一个线程
Thread mythread = new MyThread();
//启动线程
mythread.start();
while (true) {
System.out.println("hello main");
}
}
}
运行这个程序,此时,idea进程
会创建一个java进程
,
此时java进程中有两个个线程 main线程(主线程)
与thread线程(未执行)。
直到主线程执行到了mythread.start();
这个语句,才真的在操作系统的底层创建出一个线程,并且开始执行这个新线程
。
这两个线程通过并行+并发
方式,可能是并行
执行,也可能是并发
执行,也可能两者一会并行,一会并发。(多个线程在CPU上调度执行的顺序是不确定的,随机的)
不管是并行执行还是并发执行,两个线程都是在同一个控制台输出,所以只能交替输出。
也可以通过匿名内部类来实现创建 Thread子类对象
public class MyThread {
public static void main(String[] args) {
//通过匿名内部类来编程线程代码
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
}
}
};
//启动线程
thread.start();
while (true) {
System.out.println("hello main");
}
}
}
实现 Runnable 接口
使用Runnable interface来描述线程入口。
run方法是Runnable接口中的抽象方法。
class MyRunnable implements Runnable {
//编写线程代码
@Override
public void run() {
while (true) {
System.out.println("hello start");
}
}
}
public class MyThread {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
//创建 Thread 类实例, 调用 Thread 的构造方法时将 MyRunnable 对象作为 target 参数.
Thread thread = new Thread(myRunnable);
//启动线程
thread.start();
while (true) {
System.out.println("hello main");
}
}
}
也可以通过匿名内部类来创建 Runnable 子类对象
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
}
}
});
thread.start();
while (true) {
System.out.println("hello main");
}
}
也可以通过Lambda表达式创建 Runnable 子类对象
public static void main(String[] args) {
//通过Lambda表达式
Thread thread = new Thread(()->{
while (true) {
System.out.println("hello thread");
}
});
thread.start();
while (true) {
System.out.println("hello main");
}
}
}
总结
以上就是今天要讲的内容,本篇文章先让大家认识线程,还会有后续文章来继续带大家了解线程这一关键先生的。
路漫漫,不止修身也养性。