java基础-多线程

后端 / 笔记 / 2021-09-25

线程,进程

什么是进程?
cpu从硬盘中读取一段程序到你内存中,该执行程序的实例就叫做进程.

什么是线程?
线程是程序执行的最小单位,在一个进程中,可以有个多个不同的线程,同时执行。

image.png

并行,并发

什么是并行?
多个水龙头同时流水。

什么是并发?
多个水龙头交错流水。

为什么要用多线程?

场景1:假如你要盖一个房子,从选材料,打地基,构建骨架,装修...这些都要你一个人完成,会非常非常耗时间。串行

什么是多线程?
  把上面的操作交给多个人去处理,模块细化,分工合作,从而提高工作效率。并行

场景2:假如你要开发一个音乐软件,如果这个音乐软件是单线程的,那么他就只能专注于做一件事情,比如说你想在听歌时打开评论,那么就必须要停掉播放器,才能去打开评论,所以这是一件很不合乎常理的事情。

  如果我们引入多线程,那么这件事情就很好解决。首先模块细化,播放器模块交给播放线程。评论模块交给评论线程,下载模块交给下载线程。这样无形之间就提高了程序的健壮性

串行,并行

串行:单线程执行,代码从上向下执行,执行效率非常低。

并行:多个线程一起执行,效率比较高。

image.png

多线程一定就快吗?

拿盖房子来说,一个人要两年,10个人可能需要1年,30个人可能需要半年....

照这么算的话,1w个人是不是几秒就能盖一个房子?

显然是不合乎常理的,较多的线程会使cpu频繁切换上下文,从而导致性能损失. 1w个人盖房子,你光是通知每个人做什么就需要很多的时间,更别说管理起这1w个人了。所以多线程不是越多越好

cpu调度

image.png

如果开启了上千个线程,而服务器是8核,那么多线程会在我们这些cpu上做上下文切换。

上下文切换
从当前任务切换到,另外的任务.

多线程应用场景

  • 客户端开发
  • 异步发送
  • 比较耗时的操作可以采用多线程
  • 多线程下载

image.png

准备好了吗~干货来了

多线程创建方式

  • 继承Thread类创建线程
  • 实现Runnable接口
  • 匿名内部类创建
  • lambda表达式创建
  • 使用callableFuture创建线程
  • 使用线程池
  • 异步注解

继承Thread类实现

实现流程

  1. 编写一个类
  2. 继承Thread类
  3. 重写run方法
  4. 创建实例
  5. 调用start方法启动线程

这里需要注意的是虽然重写的是run方法但是启动线程必须使用,start方法。

package thread;

import java.time.Duration;

class BeepThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Beep~" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadCreate {

    public static void main(String[] args) {

        new BeepThread().start();
    }
}

Beep~0
Beep~1
Beep~2
Beep~3
Beep~4
Beep~5
Beep~6
Beep~7
Beep~8
Beep~9

实现Runnable接口实现

实现流程

  1. 编写一个类
  2. 实现Runnable
  3. 重写run方法
  4. new Thread实例
  5. 传递自己的类
  6. 调用start方法

需要注意的是要用Thread类包装。


class AlarmThread implements Runnable {

    @Override
    public void run() {
        System.out.println("di~");
    }
}

public class ThreadCreate {

    public static void main(String[] args) {
	// 这里需要注意要用Thread类包装
        new Thread(new AlarmThread()).start();
    }
}

di~

匿名内部类实现

这里纯粹就是java基础知识,没什么好说的。

public class ThreadCreate {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类创建线程");
            }
        }).start();
    }
}

匿名内部类创建线程

lambda表达式实现

这里是java8的基础知识,前面文章说过,这里不做详细概述.


public class ThreadCreate {

    public static void main(String[] args) {

        new Thread(() -> {

            System.out.println("lambda表达式创建线程");
        }).start();
    }
}

lambda表达式创建线程

Callable 和 Future实现

Callable 和 Future 和上面几种方式不同的是,可以拿到返回值,底层基于LockSupport

创建流程

  1. 编写一个类
  2. 实现Callable接口 这里的T代表返回值类型
  3. 重写call方法

调用流程

  1. 创建实现callable接口类的实例
  2. 创建一个FutureTask实例用来接受返回值
  3. 创建Thread实例,将futuretask实例作为参数传递进去
  4. 调用Thread实例start方法启动线程.
  5. 调用FutureTask实例get方法获取线程执行结果

class SoloThread implements Callable<String> {

    // 当前线程需要执行的的代码 返回值

    @Override
    public String call() {
        try {
            Thread.sleep(3*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "gugua~gugua~";
    }
}


public class ThreadCreate {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
	// 创建callable实例
        SoloThread soloThread = new SoloThread();
	// 用来接受返回值
        FutureTask<String> soloFutureTask = new FutureTask<>(soloThread);
	// 传递futuretask实例
        new Thread(soloFutureTask).start();
	// 通过get方法获取返回值
        System.out.println(soloFutureTask.get());
    }
}

孤寡~孤寡

gugua~gugua~

使用线程池实现

线程池原理:复用机制

创建方式

  1. Executors.newXXX
  2. 调用execute方法来执行
  3. shutdown来关闭线程池
package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolTest {
    public static void main(String[] args) {

        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            System.out.println("我是子线程1");
        });
        executorService.execute(() -> System.out.println("我是子线程2"));

        executorService.shutdown();

    }
}

我是子线程1
我是子线程2

spring异步注解创建

创建流程

  1. 开启spring异步注解@EnableAsync
  2. @Async标注到要异步的方法上

开启异步支持

image.png