多线程基础概念1 | 线程的三种创建方式

1.多线程介绍

学习多线程前,我们先了解几个关于多线程的概念。

1.1、进程:当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且进程之间是相互独立存在的。
如打开电脑的任务管理器,点击进程栏,qq,IDEA,谷歌浏览器,网易云音乐就是四个进程。
操作系统进行资源分配的最小单元是进程
在这里插入图片描述

1.2、线程: 线程是进程中的一个最小执行单位,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。如下图的IDEA,就同时运行了四个线程。
操作系统进行运算调度的最小单元是线程
在这里插入图片描述

总的来说:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

1.3、串行:顾名思义,“串” — 单个线程把多个任务像串一样串起来,执行的时候只能一个一个依次执行,不会在时间上同时执行多个任务

1.4、并行:一样的,从名字来看,齐头并进。比如同时下载多个文件,这是在同一时刻同时发生的,存在时间重叠。


了解到以上基本概念之后,我们来看多线程是什么。

举个例子,打开网易云音乐,就是开启了一个进程。而在这个进程里有很多功能,听歌,搜歌,看mv等。
如果是单线程,我们听歌的时候想搜歌,只能等歌曲结束,才能进行搜歌操作,这是有执行顺序,不能同时进行的。
而如果是多线程,我们可以一边听歌,一边搜索歌曲,还可以一边看评论区。 这几个操作可以同时进行,互不影响,没有执行顺序。

这就是一个进程运行时产生了多个线程。

2.程序运行原理

现代操作系统比如UNIX,Linux,Windows等,都是支持“多任务”的操作系统。所谓的多任务,就是操作系统可以同时运行多个任务。
操作系统(如Windows、Linux)的任务调度是采用时间片轮转抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。
任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态等待下一个属于它的时间片的到来。

这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发。

并发与并行
现在,多核CPU已经非常普及了,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。对于

并发:多个进程在一个CPU下采用时间片轮转的方式,在一段时间之内,让多个进程都得以推进,称之为并发(假同时)
并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行(真同时)

计算机操作系统中把并行性和并发性明显区分开,主要是从微观的角度来说的,具体是指进程的并行性(多处理机的情况下,多个进程同时运行)和并发性(单处理机的情况下,多个进程在同一时间间隔运行的)。
并发与并行类似于工厂中的流水线,要扩大产量,1是考虑建造多个工厂,这就是并行。2是考虑每个工厂中新增流水线,这就类似并发


3.用java实现一个进程

方式一、通过继承 Thread 来创建一个线程类,该方法的好处是 this 代表的就是当前线程,不需要通过Thread.currentThread() 来获取当前线程的引用。

package MyThread;

public class MyThread {
    public static void main(String[] args) {
        Thread2 t = new Thread2();
        t.start();
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            //打印当前进程名字(如自己没设置名字,系统默认生成)
            System.out.println(currentThread().getName());
        }
    }
}
Thread-0

方式二、通过实现 Runnable 接口,并且调用 Thread 的构造方法时将 Runnable 对象作为 target 参数传入来创建线程对象
该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用。

package MyThread;

public class MyThread {
    public static void main(String[] args) {
        Thread t = new Thread(new myRunnable());
        t.start();
    }
  static class myRunnable implements Runnable {
      @Override
      public void run() {
          System.out.println(Thread.currentThread().getName()+"在运行");
      }
  }
}
Thread-0在运行

方式三、使用Callable创建线程(Future/FutureTask + Callable)

使用Callable 创建线程,有返回值,可以获取到线程的执行结果

public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("callable对象");
                return "OK";
            }
        };
        //FutureTask任务类,实现了Runnable, Future接口。传入callable
        FutureTask<String> task = new FutureTask<>(callable);
        //通过线程启动
        new Thread(task).start();

        //main线程阻塞等待,需要等上边线程执行完毕并获取到返回值
        String str = task.get();  //task.get()获取返回值
        System.out.println(str);
        System.out.println("====================");
        //线程池可以传入callable对象,返回值为Future类型
        ExecutorService pool = Executors.newFixedThreadPool(4);
        Future future =  pool.submit(callable);
        System.out.println(future.get());  //main阻塞等待
    }
callable对象
OK
====================
callable对象
OK

最后,再用一个小例子体会一下多线程的优点 - 增加运行效率
在这里插入图片描述

package ThreadAdvantage;

public class ThreadAdvantage {
    //线程数目
    private static final int NUM = 5;
    //计算次数
    private static final int COUNT = 10_0000_0000;
    //测试:计算 a 的值
    private static void increment(){
        int a = 0;
        for(long i = 0; i < COUNT; i++){
            a++;
        }
    }
    //串行(依次执行)
    private static void serial(){
        long start = System.nanoTime();
        for(int i = 0;i < NUM;i++){
            increment();
        }
        long end = System.nanoTime();
        System.out.printf("串行运行时间:%s毫秒",(end-start)/1000/1000);
    }

    //并发(有时候并发既表达并发,也表达并行)
    private static void parallel(){
        long start = System.nanoTime();
        for(int i = 0;i < NUM;i++) {
            //开启五个新线程,分别运行increment
            new Thread(new Runnable() {
                @Override
                public void run() {
                    increment();
                }
            }).start();
        }
        //若活跃线程大于1,即并发没结束,当前线程让步,继续执行并发线程
        while (Thread.activeCount() > 1){
            Thread.yield();
        }
        long end = System.nanoTime();
        System.out.printf("并发运行时间:%s毫秒",(end-start)/1000/1000);
    }

    public static void main(String[] args) {
        serial();
        System.out.println();
        parallel();
        System.out.println();
    }
}

运行结果:

Connected to the target VM, address: '127.0.0.1:52532', transport: 'socket'
串行运行时间:3156毫秒
并发运行时间:1180毫秒
Disconnected from the target VM, address: '127.0.0.1:52532', transport: 'socket'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值