一·进程与线程的基本概念:
1.进程:通俗来说,一个跑起来的应用程序就被成为进程。官话是这么解释的:进程是操作系统中资源分配的基本单位。进程是操作系统对正在运行程序的一种抽象,可以将进程看作程序的一次运行。
介绍线程之前,我先想给大家讲讲为什么要有线程。
多进程编程已经可以解决很多并发编程的问题了,CPU的很多资源也可以调度起来了。
但是,在资源分配和回收上,多进程还是有很多短板。主要有以下三点:
1.创建一个进程,开销比较大。
2.销毁一个进程,开销比较大。
3.调度一个进程,开销也比较大。
大家是不是听的云里雾里,那我来将抽象实例化一下~
我叫王五,经营着一个海鲜食品加工厂,由于质量过硬,物美价廉,订单越来越多,单一个厂子,已经无法满足业务需求了。因此我先想出了第一个方案,再租一片场地,再买入一套生产流水线,再配套一个完整的物流系统和供销线路。但我发现预算实在是太大了,成本非常高,于是我又想,直接在原先的场地里,直接引进多条生产流水线。因为我的其他资源是相当完善的,只要花买入新生产流水线的钱即可。成本大大降低,非常划算。
于是,类似的思路,为了更节省资源的并发编程开发,线程应运而生。在解决编程问题的同时,让创建销毁调度的速度更快。因此线程也叫做"轻量级进程"。以此我们引出线程的概念。
2.线程:线程是操作系统中调度执行的基本单位。一个线程是一个“执行流”(我们提到的生产流水线),每个线程之间都可以按照顺序执行自己的代码,多个线程“同时”执行多份代码。
二.进程与线程的区别(重要的面试问题):
1.总概括:
进程是包含线程的,二者是包含关系。一个进程可以有一个或者多个线程。
2.线程安全性:
进程模型:天然是资源隔离的,不容易触发安全问题。进行进程通信的时候,多个进程访问同一个资源(共同使用一个公共空间),可能会出现问题。
线程模型:天然就是资源共享的,多线程争抢同一个资源,非常容易触发安全问题。
3.进程是操作系统中资源分配的基本单位,线程是操作系统中调度执行的基本单位。
根据前面实例理解,一个进程相当于一个完整的工厂,需要各方面资源的完善。
而一个线程相当于一个生产流水线,是生产商品的基本单位。
4.进程都拥有自己独立的虚拟地址空间,有多个进程时,其中一个进程崩溃了并不会影响其他进程。但是线程是多个线程共用一个内存空间(把鸡蛋放在了一个篮子里),当一个线程崩溃,会影响到其他线程,甚至是导致整个程序的崩溃。
5.进程和线程都是应用于处理并发编程这样的场景。但是进程频繁创建和释放资源的效率低。相比之下,线程更轻量,创建和释放资源的效率更高。
6.进程的上下文切换速度比较慢,而线程的上下文切换速度比较快。(前情回顾:上下文及存档读档的过程)。
注意:一味的增加线程数量,是不可以一直提高效率的。因为CPU的核心数量是有限的。线程太多,线程之间彼此争抢,会导致线程无法充分利用CPU资源,不少的开销反而浪费在线程调度上了。(好比有一桌饭,为了让这桌饭尽快被吃完,你增加人数,但是人数太多的时候,桌的座位是有限的,大家都抢着吃,导致谁都吃不了多少,总体的进程速度也就被拖慢了。)
三.基础的实现多线程编程的方法:
背景知识:1.java操作多线程,最核心的类是Thread类,不需要import其他包。java.lang包下的类,是最基本的类。
提示:一定要自己动手体验代码是如何打的,因此我只会在第一题为大家提供代码段,其余的提供图片,鼓励大家不要光看不练,更要动手自己打,不要复制。
一.继承Thread类,重写run
1.代码实现:
package thread;
class MyThread extends Thread{
@Override
public void run() {
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class thread {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.start();
while (true){
System.out.println("hello world");
Thread.sleep(1000);
}
}
}
2.代码说明:
(1).start方法与run方法的说明与区别:
start是真正创建了一个新的线程(或者说启动一个新的线程),而run只是描述了新线程要干的事情。 同时要注意,start是没有调用run方法的,而是由新的线程来执行run所描述的任务。
(2).运行结果说明:从运行结果可以看出,hello world与hellow thread均得到了打印。但是我们发现并没有什么顺序或者规律。这里,大家就要想起我们前面讲的,线程对CPU资源是抢占式的,谁抢到资源谁先执行(桌子上的人,谁先抢到饭谁先吃)。(其实本质上并非是随机的,是有一套算法函数的,但是自变量实在太多太多,就认为是随机的了。)
二.实现Runnable接口:
1.代码实现:
代码结果:
2.代码说明:大家注意,main本身也是一个线程,我们的新线程(t.start())就是main这个主线程调用的。
三.使用匿名内部类,继承Thread
本质上与第一个方法相同,只不过呈现的方式不同,本质上都是继承Thread类的结果。
1.代码实现:
代码结果:
四.使用匿名内部类,实现Runnable
此方法本质上与第二种一样,呈现方式不同,本质上都是实现Runnable接口的结果。
1.代码实现:
2:代码结果
五.使用lambda表达式(最简单的实现方法,比较推荐)
1.代码实现:
2.代码运行结果: