一文搞懂进程&线程

        小玉这几天在复习多线程篇知识,最近有点偷懒了,博客要常常写!加油!  那么接下来就跟着小玉来入门多线程吧.....


目录

1.什么是进程?

2.什么是线程?

2.1进程&线程的区别是什么?

3.创建多线程的几种方法

3.1方法一:继承Thread类

3.2方法二:实现Runnable接口 

3.3方法三:采用匿名内部类 

3.3.1继承Thread,采用匿名内部类 

3.3.2实现Runnable,采用匿名内部类

3.4方法四:采用Lambda表达式 (最推荐使用)

4.Thread常见构造方法及属性 

         



1.什么是进程?

        在操作系统没有引入进程之前,由于CPU一个核心一次只能执行一个程序,所以多个程序只能顺序执行,像以前的手机QQ退出后就无法接受消息了.....CPU的速度很快,磁盘、网路等IO的速度很慢,造成CPU会有大量空闲的时间,此时CPU的利用率很低,为了解决CPU的利用率低的问题,操作系统引入了进程以及中断处理,实现了在同一时间段内,多个程序的"并发"执行,这个程序执行一点,那个程序执行一点,这样并发交替的执行大大提高了CPU的利用率。
        简单来说:跑起来的程序就是进程!一个.exe文件就是一个进程,例如QQ,微信,CCTALK......

2.什么是线程?

        一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码.就例如QQ:QQ是进程,但是QQ可以同时兼顾聊天视频,看朋友圈,发图片等等功能,这些功能可以同时执行,这即是线程. 

        线程很好的解决了"CPU资源浪费的情况",可以在CPU快速调度的时候,充分减少等待时间,提高计算机的使用效率.现如今,"并发编程"已经成为主流,学习多线程也是刚需!

2.1进程&线程的区别是什么?

        很多朋友在了解完进程和线程这两概念之后就陷入迷茫,我们下面就来探索一下他们之间的区别究竟在哪........

  • 进程包含线程

        好,第一点盖棺定论!一个包含关系直击痛处!  一个进程至少包含一个线程,也可以包含多个线程,线程不可脱离进程存在.那么既然包含关系存在那么我们能得出有效结论:

创建线程比创建进程更快.
销毁线程比销毁进程更快.
调度线程比调度进程更快.
(即:线程比进程更轻量~~~)

  • 线程是独立的执行流,同一个进程的多个线程之间共享同一份资源

        OK! 我们如果将进程比喻成一个"工厂",那么线程就是工厂的"流水线",他们之间可以并发执行.那么"共享资源"到底在说什么? 资源----即内存空间文件描述附表. 

        只有这个进程的第一个线程创建的时候才会去申请资源,这些资源申请完之后,后续在该进程里创建新的线程之后就无须再申请了.

  • 线程是操作系统资源调度的最小单位,进程是操作系统资源分配的最小单位. 

        有了前面的结论铺垫我们也能得出这个结论,我们之前说的进程也可以进行调度,这里指的是这个进程只有一个线程的时候,操作系统调度的其实是这个进程的线程,所以我们才会以为进程在频调度...... 

  • 进程具有独立性和隔离性.一个进程挂了不影响其他进程,但是一个线程挂了可能会吧进程和进程里的其他线程都带走

        就比如QQ哪天挂了,但是微信不受影响,但是要是QQ发不了消息了,可能就进入死机状态,看朋友圈打视频可能都干不了了..........

        好,以上就是进程&线程的区别,大家可以仔细体会一下,这里很好理解,希望去哪个大家不要想的太多,复杂化了........


3.创建多线程的几种方法

        那么我们了解完了进程&线程,在Java中我们重点学习多线程,那么如何创建一个多线程呢?我们打开IDEA:像往常一样,建一个普通的文件:

3.1方法一:继承Thread类

        在类外创建一个类并且继承Thread类,重写run();这里实际上是针对原本的Thread库实现扩展,在里面我们打印上一句"hello t ";在下面public类里我们创建子类实例t,并且调用t.start() 创建并启动新线程!

        上述代码中我们涉及到的进程是Java进程,其中包含两个线程:主线程main 和我们创建的线程t.接下来我们再感受一个什么叫:每个线程都是独立的执行流.

         我们设置两个死循环,如果是以前的思维:那么程序进入main之后,遇到t.start(),会在这个循环里出不来,那样就不可能执行到下面的第二个死循环了,但是我们观察控制台:

        我们可以发现:这里能交替打印出 hello t 和 hello main 如果想更清楚一点,我们可以设置休眠: 

此时在观察控制台: 

        我们就能更清楚的看到交替的过程了,关于为什么有时候先打印hello t有时候先打印hello main,这里小玉想说:系统的调度是无序/随机的,虽然我们各自设置了休眠,但是我们无法控制哪个线程先执行. 这里就能体现那句话了,什么来着?每个线程都是独立的执行流.

3.2方法二:实现Runnable接口 

        那么介绍完了第一个方法,还有的就是:实现Runnable接口,看代码:

class MyThread2 implements Runnable{
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyThread2 myThread2 = new MyThread2();
        Thread t = new Thread(myThread2);
        t.start();
        
        while (true){
            System.out.println("hello main ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

        结果跟方法一是一样的,这里就不放了,朋友们自行实践一下吧~~~ 


这里需要注意! 重写的是run()方法,但是我们启动新线程却是调用start()方法,可否启动新线程直接调用run()呢? 

run() 与start() 的区别: 

  • run()方法可以视作新线程的入口方法,就类似于主线程的main()方法一样,在我们调用start()方法是会自动调用run()方法,因为这是我们重写的方法.
  • run()方法不会创建新线程,只是线程的入口指示牌,但是start()方法会创建一个新线程! 

我们来看例子吧:

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

//        t.start();
        t.run();

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

只需要改动一行代码,我们观察控制台:

我们发现,hello main不打印了,为什么?看图解:

此时我们的线程只有主线程main,main进入第一个死循环之后就出不去了,只能一秒一个hello t...... 


你们理解什么是:线程是独立的执行流这句话了吗? 

3.3方法三:采用匿名内部类 

        所谓的匿名内部类就是定义在类里面的类,过两天小玉深度学习一下,写一篇博客吧....小玉也不是很懂...... 

3.3.1继承Thread,采用匿名内部类 
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                while (true){
                    System.out.println("hello t");
                    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();
            }
        }
    }
}

运行结果同方法一 

3.3.2实现Runnable,采用匿名内部类
public class ThreadDemo4{
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                while (true){
                    System.out.println("hello t");
                    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();
            }
        }
    }


}

注意了这里不需要implements Runnable直接在括号里new Runnable即可;效果相同~~~ 

大括号{放在哪里就是针对哪个类的内部类,大家区分清楚.

3.4方法四:采用Lambda表达式 (最推荐使用)

Lambda 表达式实际上就是一个匿名函数,本质上就是一个连名字都不配拥有的函数,用完就丢,一次性.

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (true){
                System.out.println("hello t");
                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();
            }
        }
    }
}

我们发现这种方法连run()都不需要写了......很方便,但是大家注意格式: 


4.Thread常见构造方法及属性 

构造方法:

属性:

关于是否是后台线程,这里贤臣共分为前台和后台线程,前台线程会阻止线程结束,当前台线程结束的时候,整个线程才能结束,这个isDaemon()方法默认是false,也就是默认是前台线程,了解即可.......


        小玉就说这么多吧......更多精彩内容小玉会努力更新的,欢迎留言讨论!再见了~~~~ 

        

         

  • 46
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨小玉_ShawYu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值