【Java 并发编程】初识多线程

前言


        到目前为止,我们学到的都是有关 “顺序” 编程的知识,即程序中所有事物在任意时刻都只能执行一个步骤。例如:在我们的 main 方法中,都是多个操作以 “从上至下” 的顺序调用方法以至结束的。

        虽然 “顺序” 编程能够解决相当大的一部分问题,但是当所有的方法全部推在一个 “队列” 里面,如果前面的方法没有执行完,后面的方法就轮不到。这样程序的运行效率必然十分低下。对于一些问题,能够同时执行程序的多个部分,就会十分方便且必要。而并发编程的设计就是为了专门解决这一类问题。


前期回顾:Java 之深入理解 String、StringBuilder、StringBuffer

文章代码:码云地址


目录

前言

并发与并行的简单区别

 并发

总结

并行

总结

进程与线程的简单区别

进程

线程

进程与线程的区别总结

多线程的理解

创建线程任务的方式

继承 Thread 类

实现 Runnable 接⼝

匿名内部类创建

lambda 表达式创建

并发与并行的简单区别

 并发

        关于并发这里举个例子:一家餐厅来了三个食客,但是厨师只有一位,怎么出餐就成了问题。<1> 按食客进店的先后顺序上(之前提到的顺序编程),那么后两位食客等的时间久了自然不乐意肯定会掀桌子。<2> 三个人的菜同时做,虽然只有一个厨师(一个CPU核心),但是有三口锅(三个线程),当一个菜烧制的差不多的时候立刻就切换到另一个菜。这样几乎可以做到三个菜的同时出餐。

        

        在计算机中,由于 CPU 的运算速度非常的快,切换的速度也非常的快,如上图,可能执行到QQ的指令2就立刻跳转到微信的线程,站在宏观角度,可以看成是同时执行(并发执行)的。

总结

        并发是指两个或多个任务在同一时间间隔内发生比如在单核CPU上运行 16 个线程,由于核数限制,这 16 个线程无法在同一时刻运行,所以CPU只能采用时间片切换的方式来运行。

并行

          关于并行这里举个例子:一家餐厅来了三个食客,但是厨师刚好有三位。这样怎么出餐就不成问题了。我们只需每桌安排一个厨师,就可完成出餐任务。

        在计算机中,这就意味着有一个CPU有多个核心,同一时刻的程序可以分配到多个CPU处理器上,实现多任务,并行执行

总结

        当有多个CPU核心时,在同一个时刻可以同时运行多个任务,这种方式叫并行比如,3核CPU可以同时运行3个线程。

        顺序编程是指多个操作按次序执行,并行编程是指多个操作同时执行,而并发编程将同一个操作分解为多个部分并允许无序执行。

进程与线程的简单区别

进程

        进程是指正在运行中的程序实例。每个进程都是一个独立的执行单元,拥有自己的内存空间和系统资源。一个进程可以包含多个线程,是程序执行的基本单位一个在内存中运行的应用程序。比如在 Windows 系统中,一个运行的 xx.exe 就是一个进程。我们打开任务管理器就可以看到此时电脑的进程。

线程

        线程是进程内可计划执行的实体。 进程的所有线程共享其虚拟地址空间和系统资源。一个进程至少有一个线程,即主线程 main;一个进程可以运行多个线程,多个线程可共享数据。

        与进程不同的是同类的多个线程共享进程的方法区资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程

进程与线程的区别总结


        线程具有许多传统进程所具有的特征,故又称为轻型进程或进程元;而把传统的进程称为重型进程,它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。

多线程的理解

这里先举个例子 ~

单进程

        猴子吃桃, 一只猴子在一间包间内需要吃一百个桃,我们知道这其实效率是很低的。那么我们可以将猴子吃桃当成是一个进程处理一个大型项目,那么这样搞会导致程序运行卡顿。所以我们需要将其优化。

多进程

        我们可以将吃完 100 颗桃子的任务分给4子猴子完成,虽然效率提升了,但是每只猴子都需要开一间包间吃桃子,这就导致了资源的开销大。将一个程序交给4个进程也是一样的道理。

多线程

        那么我在一个包间让多个猴子完成吃完桃子的计划,这样的安排是最合理的。既保证了效率,也节省了空间。那么猴子是不是越多效率越快呢?其实并不然,因为座位有限,当座位无法容纳更多的猴子,此时再添加猴子只会让它们相互 “争夺” 吃桃子的位置,从而影响吃桃的效率。

        多线程也是如此:一个进程中可以拥有很多线程来辅助程序的运行,这里提升效率的因素是 “并行”。但是如果线程太多超过了CPU的核心数目,此时就无法完成所有线程的并行执行,就会势必会造成严重的 “线程冲突”。

创建线程任务的方式


继承 Thread 类

        线程可以驱动任务,因此你需要一种描述任务的方式。这可以继承 Thread 这个线程类。要想定义任务,我们只需要重写 Thread 类中的 run() 方法即可,使得该任务执行你的命令:

class MyThread extends Thread{
    @Override
    public void run() {
       while(true){
           // 即将创建出的线程,要执行的逻辑
           System.out.println("Hello");
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
    }
}

public class Demo1{
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        // 创建线程
        thread.start();

        while(true){

            System.out.println("World");

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

        }
    }
}

运行截图:

        <1> 任务中的 run() 方法通常总会以某种方式的循环,使得任务一直执行下去直到不需要,所以需要设置循环条件。通常 run() 会被写成无限循环形式,这就意味着,除非有某个条件使得 run() 停止,否则它将会永远的运行下去。

        <2> 由于该程序是死循环,为了更好的观察加上了sleep。而我们要捕获 sleep 方法有可能会抛出 InterruptedException 异常。一般来说,中断请求终止一个线程。相应的,如果抛出 InterruptedException 时,run() 方法就会退出。

        <3>

World
Hello
Hello
World
Hello
World
Hello
World
...

        通过打印结果我们可以发现,这个线程的输出是交错的,说明它们在并发的进行。

        <4> 以上的程序一共有两个线程,一个是 main 线程,一个是我们用 Thread 创建的线程。但是如果我们直接调用 Thread 的 run() 的方法,只会在 main 线程中执行这个方法,而没有开辟新的线程。

        <5> 虽然以上方法可以创建线程但是不推荐使用,我们应该将并行运行的任务与运行机制解耦合。如果有多个任务,为每个任务分别创建一个线程开销太大。

实现 Runnable 接⼝

class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程需要完成的逻辑
        while(true){
            System.out.println("Hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    };
}

public class Demo2 {
    public static void main(String[] args) {
         // 创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为参数
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();
        while (true) {
            System.out.println("World");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

匿名内部类创建

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

 

lambda 表达式创建

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

烟雨长虹,孤鹜齐飞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值