一.并发编程(多进程)
缺点:1.消耗资源多
2.速度更慢
解决方案: 轻量级进程 => 线程(Thread)
线程只创建了一个pcb,但没有分配后续的内存、硬盘和资源
线程搞出来,是为了执行一些任务,执行任务又需要消耗这些硬件资源,所以就可以创建的还是进程,创建进程的时候,把资源都分配好,后续创建的线程,让线程在进程的内部(进程和线程之间的关系,可以认为是进程包含了线程),后续进程中新的线程,直接复用前面进程这里创建好的资源
其实,一个进程至少要包含一个线程,最初创建出来的这个,可以视为一个只包含了一个线程的进程(此时创建的过程需要分配资源,此时第一个线程的创建开销可能比较大的),但是后续再在这个进程里创建线程,就可以省略分配资源的过程,资源已经是有了的
使用多进程本身已经可以并发编程了,但是进程比较重,创建和开销比较大(需要申请和释放资源)
引入线程,能更有效的解决上述问题
所谓的线程,也可以称为轻量级线程
一个进程可以包含一个进程,也可以包含多个线程,这个线程中的多个线程,共同复用了多个进程中的各种资源(内存和硬盘)
但是这些线程各自独立在cpu上进行调度
因此线程就可以既能够完成“并发编程”的效果,又可以以比较轻重的方式来运行
二.线程
线程(PCB),同样也是通过PCB来描述的
PCB => 进程控制快
此时,一个PCB对应到一个线程,多个PCB对应一个线程了
PCB中的内存指针、文件描述符,同一个进程的多个PCB中,这俩字段的内容是一样的,但是上下文、状态、优先级和记账信息...支持调度的属性,则这些PCB每个人都不一样
同一个进程中的这些线程共用同一份资源(内存+硬盘)
但是每一个线程独立去cpu上调度(状态、上下文、优先级、记账信息等等....各有各自的一份)
进程,是操作系统进行资源分配的基本单位
线程,是操作系统进行调度执行的基本单位
三.关于进程与线程的面试题(高频)
题目:谈谈进程与线程的区别和联系:
1.进程包含线程,都是为了实现并发编程的方式,线程比进程更轻量
2.进程是系统分配资源的基本单位,线程是系统调度执行的基本单位
创建进程的时候把分配资源的工作做了,后续创建线程,直接共用之前的资源即可,节省了申请资源的开销
3.进程有独立的地址空间,彼此之间不会相互影响到,进程的独立性使得系统的稳定性得到有效地提升
多个线程共用这一份地址空间,一个线程一旦抛出异常,就可能导致整个进程异常结束,意味着多个线程之间容易相互影响
线程是更轻便,但在互联网圈子,高并发的服务器,要处理的并发量太多了,频繁的创建线程/销毁线程,会导致开销巨大
解决方案:
1.“轻量级线程”,也叫协程/仟程
2.“线程池”
池(pool),其实是计算机中非常典型的思想方法
把一些要释放的资源,不要着急释放,而是放在一个“池子里”,以备后续使用
申请资源的时候,也是要把提前申请的资源申请好,也放在一个“池子里”,后续申请也方便
四.使用线程输出“hello world”
首先创建一个Package,命名为thread,再在thread中创建新的java class为Demo1
package thread;
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello world");
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread= new MyThread();
myThread.start();
}
}
4.1 重点1
重写方法中的run()方法,相当于线程 的入口方法,线程具体跑起来后,要做任何事情都要通过run来描述
4.2 重点2
主函数中的start()就是在创建线程(这个操作就会在底层调用操作系统提供“创建线程”的API,同时就会在操作系统内核创建出对应的pcb结构,并且加入到对应的链表中)
若将myThread.start()改写成myThread.run()也可以,但run只是上面的入口方法,即普通方法,并没有调用系统api,也未创建出真正的线程来
五.Java中,通过Thread类创建线程的方式,还有很多种写法
5.1 创建一个类,通过继承(class)Thread,重写run方法
package thread;
class MyThread extends Thread{
@Override
public void run() {
while(true) {
System.out.println("hello world");
try {
//这里只能try catch,不能throws
//此处是方法重写,对于父类的run方法;来说,就没有throw xxx 异常这样的设定
//在重写的时候,也就不能throws异常了
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread= new MyThread();
myThread.start();
while(true){
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
5.2 创建一个类,实现(实现interface)Runnable,重写run方法
package thread;
class MyRunnable implements Runnable{
@Override
public void run() {
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//使用Runna的方式创建线程
public class Demo2 {
public static void main(String[] args) {
MyRunnable runnable=new MyRunnable();
Thread t=new Thread(runnable);
//将runnab作为构造方法的参数传递到Thread的构造方法里
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}