进程和线程的区别与联系

1.什么是线程

 一个线程就是⼀个"执行流",每个线程之间都可以按照顺序执行自己的代码,多个线程之间"同时"执行着多份代码.

2.为什么引入线程(Thread) 

当前的cpu都是多核心的cpu,需要通过一些特定的编程技巧(并发编程),把要完成的任务拆解成多个部分,并分别让他们在cpu不同的核心上运行。如果没有进程,就会让任务只在一个核心上运行,会让这个核心负担重,任务完成也不高效,且其他核心是闲着的,那么为什么不利用起来呢?
而"多进程"编程的模式,就可以起到"并发编程"的效果,因为进程可以被调度到不同的 cpu 上运行。

虽然多进程编程可以解决上述问题,但又带来了新的问题。 

在服务器开发的圈子里,这种并发编程的需求场景是非常常见。作为一个服务器,要能够同时给多个客户端提供服务,如果同一时间来了很多客户端,但服务器却只能利用一个 cpu 核心工作,速度就会比较慢。

那么如何解决这个问题呢?

把每个客户端连上服务器,服务器都创建一个进程,给客户端提供服务这个客户端断开了,服务器再把进程给释放掉。但是,如果这个服务器频繁的有客户端来来去去,那么服务器就也需要频繁创建/销毁进程。单个一次创建/销毁进程,其实并不会给服务器造成什么太大的负担,就怕频繁的创建销毁进程,这时候服务器的响应速度就会变慢

而引入线程最主要的目的就是为了解决上述"进程"太重量(创建/销毁 开销比较大)的问题。 

线程也称为“轻量级进程”,与进程相比,线程的创建/销毁开销就比较小。

线程,可以理解成"进程的一部分”。一个进程中,可以包含一个线程或者多个线程,使用 PCB 这样的结构体来描述进程。

更详细的说法是 描述一个进程的PCB 是由 若干个描述线程的PCB 联合在一起的。

进程是系统资源分配的基本单位,线程是系统调度执行的基本单位

 为什么说线程创建/销毁的开销比进程更小?
是因为创建进程可能要包含多个线程,而这个过程中,涉及到资源分配/资源释放。
但创建线程相当于资源已经有了,省去了资源分配/释放步骤了

就前文谈到 客户端—服务器 的问题,每个客户端连上来之后,服务器给他分配一个进程处理,但现在引入线程了,就可以每个客户端给他分配一个线程来处理,就能起到优化效果了。

省去资源分配/释放的步骤指的是什么?

同一个进程包含 N 个线程,这些线程之间是共用资源。只有你创建第一个线程的时候(也是创建进程的时候),才去进行资源申请操作后续再创建线程,后续都没有申请资源的过程。

3.理解多线程

这里举个例子来帮助理解,

比如要 饭店要人在包间消灭掉一百只烤鸡,如果一个人吃,很慢。

1).引入多进程的方案
再找一个人分担,两人同时各吃50只,但需要多开一个包厢,多给一张桌子,这花费也不低。

2).引入多线程的方案

让两个人在同一个包间吃,依然一人五十只鸡,但是不需要额外的空间和桌子,空间成本降低,但吃鸡效率并没有上来。

 3).引入更多的线程

也就是多叫些人来一起吃鸡,那么此时吃鸡效率就能上来了,十个人吃一百只鸡就是原来一个人吃一百只效率的10倍。也就是随着线程引入的增多,每个线程要完成的任务量减少了,整体的速度就会更快。

4).能否无线引入线程

吃鸡的人越多,虽然效率越快,但是空间有限,桌子也只有那么大,超出了承载能力,就会出现问题。比如很多人无法凑到桌子边上,没法和正在桌子上的人并发的执行任务,并且外围的人往里挤,就可能把正在吃鸡的人给打断。


线程安全问题:
1.这个过程中,多个人之间是可能会产生冲突的。比如1号和2号同时看上了同一个大鸡腿,但两人互不相让,导致两个人就发生冲突,打起来了,也就是程序出现 bug,即出现了线程安全问题。

2.如果某个人, 想吃的总吃不到,不太开心,然后不想吃了,他直接掀桌!!! 这时候就使别的人也吃不成了。
如果一个线程抛出异常,并且没有很好的捕获处理好,就会使整个进程退出,其他线程也就没有了。
这也多线程编程的值得关注的难点,一个线程出问题,会影响到别的线程。

相比之下,进程和进程之间独立性更好。一个进程挂了,一般不会影响到其他进程。

4.多线程代码的常用写法

1.继承Thread,重写run

class MyThread extends Thread{
    public void run(){
        while (true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        Thread t=new MyThread();

        // 没有创建出新的线程, 就是在主线程中执行上述 run 中的循环打印.
        t.start();//hello thread 和 hello main 并发执行
//        t.run();//只有 hello thread

        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }

    }
}

start 和 run这两个方法的区别是什么呢?

作用功能不同:

  1. run方法的作用是描述线程具体要执行的任务
  2. start方法的作用是真正的去申请系统线程

运行结果不同:

  1. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
  2. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。

调用start方法:                                                调用run方法:

                       

2.重写Runnable方法,重写run,把Runnable实例传入到Thread中

class MyRunnable implements Runnable{
    public void run(){
        while (true){
            System.out.println("hello thread");
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }


        }

    }
}

public class Demo2 {
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable());

        t.start();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }

    }
}

3.针对 1的变形.使用匿名内部类, 继承 Thread 并重写 run.

public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            public void run(){
                while (true){
                    System.out.println("hello thread");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }

            }

        };

        t.start();

        while (true){
            System.out.println("hello main");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }

    }

}

4.实现 Runnable, 重写 run, 使用匿名内部类

public class Demo4 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
                public void run() {
                    while (true) {
                        System.out.println("hello thread");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }

                }
        });

        t.start();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

5.使用 lambda 表达式

public class Demo5 {
    public static void main(String[] args) {
        Thread t=new Thread(() -> {
            while (true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });

        t.start();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

}

上述每种方法执行的结果都是大同小异,就是hello main和hello thread 并发执行,并且是抢占式执行,你也不知道hello thread 的下一句还是hello thread 或者是hello main。

进程是操作系统进行资源分配的基本单位,而线程进程内的一个执行单元,也是进程内的可调度实体。一个进程可以包含多个线程,而线程共享了进程的某些资源。进程线程之间存在以下区别: 1. 本质区别进程是操作系统进行资源分配的基本单位,而线程是独立调度分派的基本单位。 2. 包含关系:一个进程内可以有多个线程线程进程的一部分,因此线程也被称为轻量级进程或轻权进程。 3. 资源开销:每个进程都有独立的代码数据空间,程序之间的切换会有较大的开销;而线程可以看作轻量级的进程,同一类线程共享代码数据空间,线程之间切换的开销小。 4. 创建销毁:创建一个进程需要遍历内存资源找到合适的内存再分配给它,销毁进程也需要遍历内存资源。而创建销毁一个线程相对不那么麻烦。 5. 内存分配:同一个进程线程共享本进程的地址空间资源,而进程之间的地址空间资源是相互独立的。 6. 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响;但一个线程崩溃可能会导致包含该线程的整个进程直接死掉。 7. 操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。 8. 执行过程:每个独立的进程有程序运行的入口、顺序执行序列程序出口,而线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值