多线程知识点小结

搞"进程"是为了满足"并发编程"这样的需求(此处的"并发"="并行"+"并发")

举个并发编程的例子,比如看直播视频,那个APP不仅需要采集主播的声音和面部,还需要收集观众的聊天信息

CPU逐渐变成多个核心了,应用程序也需要作出对应的调整,来让代码可以把多个核心充分利用起来

在诺基亚时代,国产山寨机同一时刻只能开一个程序,比如QQ,如果等消息的时候切出来想看个小说,那就收不到消息了

多进程已经很好地实现了并发编程的效果

但是这里有个缺点:进程太重了(1.消耗资源更多   2.速度更慢)

如果进程创建销毁不频繁,就还好,一旦需要大规模创建和销毁进程,那开销就比较大了,开销体现在我们需要给进程分配资源,这是需要时间的,比较耗时.

于是有聪明的程序员想了个办法,能不能在创建进程的时候,只分配一个简单的pcb,而不去分配后续的这些内存硬盘资源,这样是不是就能减少时间消耗?这样是否能够并发的执行任务又能够提升创建/销毁的速度?--------这就叫做轻量级进程(线程(thread))

但是线程只创建了一个pcb,没有分配后续的内存,硬盘等资源,而执行任务需要消耗这些资源,所以我们创建的还是进程,在创建进程的时候就把资源分配好,后续创建新的线程就让线程在进程的内部直接共同复用前面进程创建好的资源,并且这些线程各自独立的在CPU上进行调度,所以我们可以认为进程包含了线程

其实一个进程至少包含一个线程,最初创建出来的这个,可以视为是一个只包含一个线程的进程(此时创建的过程需要分配资源,此时第一个线程的创建开销可能是比较大的)

但是后续在这个进程里创建线程,就可以省略分配资源的过程,资源是已经有了的

线程同样也是通过pcb来描述的,一个pcb对应一个线程,多个pcb对应一个进程

pcb中的内存指针,文件描述符表,同一个进程的多个pcb中,这俩字段的内容都是一样的,但是上下文,状态,优先级,记账信息...支持调度的属性,这些pcb则每个人都不一样了,这是因为同一个进程中的这些线程公用同一份资源(内存+硬盘......),但是每个线程独立去CPU上调度(状态,上下文,优先级,记账信息等......各自有各自的一份)

还有一点就是如果线程太多,线程调度的开销,反而会拖慢整个程序的效率

所以我们说:

进程是操作系统进行资源分配的基本单位

线程是操作系统进行调度执行的基本单位

一旦某个线程,执行过程中出现异常,并且整个异常还没有被很好地处理,此时就可能导致整个进程直接终止(进程中所有线程也就随之终止了)

谈谈进程和线程的区别和联系

1.进程包含线程,都是为了实现并发编程的方式,编程比进程更轻量

2.进程是系统分配资源的基本单位,进程是系统调度执行的基本单位                                                     创建进程的时候把分配资源(虚拟地址空间,文件描述符表)的工作给做了,后续创建线程,直接公用     之前的资源即可

3.进程有独立的地址空间,彼此之间不会互相影响到,进程的独立性=>系统的稳定性                               多个线程公用这一份地址空间,一个线程抛出异常,就可能导致整个进程异常结束,多个线程之间容     易互相影响

线程是更轻量,但是也不是没有创建成本,在互联网圈子,高并发的服务器,需要处理的并发量太多了,非常非常频繁地创建/销毁线程,开销不可忽视.所以又想了两个办法

1."轻量级线程"=>协程/纤程                                                                                                                    Java标准库目前还没有内置,有一些第三方库,实现了协程                                                                  Go里天然支持协程(用起来方便),这也是Go能火起来的原因

2."线程池"  池(pool)其实是计算机中非常经典的思想方法                                                                      把一些要释放的资源,不要急着释放,而是先放到一个"池子里",以备后续使用                                      申请资源的时候,也是先提前把要申请的资源申请好,也放到一个"池子里",后续申请会比较方便

线程本身是操作系统提供的概念,操作系统也提供了一些API供程序员来使用(Linux,pthread)

Java中,就把操作系统的API又进行了封装,提供了thread类,学习这个类就可以完成多线程编程

class MyThread extends Thread{
@Override
public void run(){//这里的run就相当于线程的入口方法,线程跑起来以后,要做啥事都是通过run来描述
System.out.println("hello world");
}
}
public class Demo1{
public static void main(String[] args){
MyThread myThread = new MyThread();
myThread.start();//此处的start就是在创建线程,这个操作就会在底层调用操作系统提供的"创建线程"的API,同时就会在操作系统内核里创建出对应的pcb结构,并且加入到对应的链表中,此时,这个新创建的线程就会参与到CPU的调度中,这个线程接下来要执行的工作,就是上面重写的run方法                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
}
}

当点击运行程序的时候,就会先创建出一个Java进程,这个进程中就包含了至少一个线程,这个线程叫做主线程,也就是负责执行main方法的线程

class MyThread extends Thread{
@Override
public void run(){
while(true){
System.out.println("hello thread");
}
}
}
public class Demo1{
public static void main(String[] args){
MyThread myThread = new MyThread();
myThread.start();  
while(true){
System.out.println("hello main");
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
}
}//运行结果是打印几个hello thread再打印几个hello main,反复交替然后死循环,这是因为start创建出了新线程,新线程和主线程main是并发执行的关系,然后它们如何执行就看操作系统的调度了


//若把main方法改成如下代码
public class Demo2{
public static void main(String[] args){
MyThread myThread = new MyThread();
myThread.run;  
while(true){
System.out.println("hello main");
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
}
}//运行的结果就是一直在打印hello thread然后死循环,这是因为run只是一个方法,并没有创建出新的线程,run方法执行完毕才会执行接下来的代码,然而它是死循环所以不会打印接下来的代码

当我们创建出线程之后,也可以通过一些方式,直观观察到的

1)idea调试器

2)jconsole

java中,通过Thread类创建线程的方式还有很多种写法

1.创建一个类,继承(class)Thread,重写run方法

2.创建一个类,实现(interface)Runnable,重写run方法

3.继承Thread,重写run,基于匿名内部类

4.实现Runnable,重写run,基于匿名内部类

5.使用lambda表达式

6.基于Callable

7.基于线程池

class MyRunnable implements Runnable{
@Override
public void run(){
while(true){
System.out.println("hello thread");
try{
Thread.sleep(millis:1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
//使用runnable的方式来创建线程
public class Demo3{
public static void main(String[] args){
MyRunnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
while(true){
System.out.println("hello main");
try{
Thread.sleep(millis:1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
//运行效果和上一个代码的demo1一样

Thread这里是直接把要完成的工作,放到了Thread的run方法中

Runnable这里,则是分开了,把要完成的工作放到Runnable中,再让Runnable和Thread配合

public class Demo3 {
    public static void main(String[] args)  {
        Thread t =new Thread(){//创建了一个子类,这个子类继承自Thread,但是这个子类没有名字,另一方面,这个类的创建是在Demo3这个类里面
            public void run (){//在子类中重写了run方法
                while (true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();//创建了该子类的实例,并用t这个引用来指向
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Thread(){//此处是创建runnable的子类(类,实现runnable),通过构造方法传给Thread
            @Override
            public void run() {//重写了run方法
                while (true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });//这个)对应的是构造方法的结束,new Runnable整个一段,都是在构造出一个Thread的参数
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo5 {//最推荐的方式
    public static void main(String[] args) {
        Thread t = new Thread(()->{//lambda表达式,本质上就是一个"匿名函数",这样的匿名函数主要就可以作为回调函数来使用,回调函数不需要我们主动调用,而是在合适的时机自动被调用
            while (true){//此处的回调函数在线程创建成功之后才真正执行
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread就是在Java中线程的代言人,系统中的一个线程就对应到Thread中的一个对象

围绕线程的各种操作,都是通过Thread来展开的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值