Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • 线程

    • 线程
      • 什么是线程安全?
      • 线程&进程
      • 为什么需要线程?
      • 创建线程方式
      • 线程状态
      • Thread#start()和run()方法区别?
      • Thread相关接口/类
      • sleep&wait
      • 线程死锁
      • 如何设置线程数量?
    • 死锁模拟
  • 共享模型

  • 非共享模型

  • 并行

  • 多线程设计模式

  • JUC
  • 线程
Nreal
2023-11-22
目录

线程

# 什么是线程安全?

对于共享资源,多个线程同时访问,都能保证资源的正确性和一致性;

# 线程&进程

线程:CPU调度执行最小单元;

进程:操作系统分配资源的最小单元;

在java中,一台JVM实例就是一个进程,执行main函数就是开启一个线程;

线程是进程中一条执行流程,同一个进程中多个线程共享地址空间和文件等资源,每个线程有各自的寄存器和栈;

# 为什么需要线程?

  1. 线程创建时间,终止时间比进程快;

    进程在创建过程中,需要资源管理信息,如:内存管理信息,文件管理信息;

    线程创建过程中,只需要共享这些资源信息;

  2. 线程切换比进程切换快;

    线程具有相同的地址空间(虚拟内存共享),同一个进程的线程都具有同一个页表,切换的时候不需要切换页表;

  3. 线程之间数据交互效率高;

    同一进程各线程间共享内存和文件资源,线程之间数据传递时,不需要经过内核;

# 创建线程方式

  • Thread方式
public static void main(String[] args) {
    new Thread(()->{
        System.out.println("线程开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程结束");
    }).start();
}
1
2
3
4
5
6
7
8
9
10
11
  • Runnable配合Thread

线程与任务分开,更容易与线程池等高级API配合,更加灵活

public static void main(String[] args) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("线程开始");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程结束");
        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • FutureTask

接受Callable类型的参数,处理有返回结果的情况

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<Integer> task = new FutureTask<>(() -> {
        System.out.println("线程运行后");
        return 100;
    });
    new Thread(task).start();
    Integer res = task.get();
    System.out.println("线程得到结果:" +res);
}
1
2
3
4
5
6
7
8
9

# 线程状态

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIME_WAITING
  • TERMINATED

# Thread#start()和run()方法区别?

start:启动一个新线程并异步执行其中的任务;

run():执行当前线程;

# Thread相关接口/类

  • 函数式接口:

    Runnable:抽象方法run(),无返回值

    Callable:call(),有返回值

  • 接口:

    Future:cancel(),get()等方法;

  • 类:

    FutureTask:异步得到结果的任务

    ExecutorService.submit()的返回类型;

    实现了Future&Runnable接口,可以作为任务直接被执行,也可以给交给Executor执行;

    构造函数可以传Runnable/Callable,传入Runnable也会转化为Callable;相当于对Callable进行封装,管理任务执行情况,存储了call方法的任务执行结果

# sleep&wait

sleep不释放锁,释放CPU;

wait释放锁,释放CPU;

# 线程死锁

条件:

  1. 互斥条件;

    多个线程不能使用同一个资源;

  2. 持有并等待条件;

    当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 B持有了,所以线程 A 就会处于等待状态;

    线程 A 在等待时不会释放自己的资源;

  3. 不可剥夺条件;

    线程持有的资源在使用期间不能被其它线程获取;

  4. 环路等待条件;

    线程 A 已经持有资源 2,而想请求资源 1, 线程 B 已经获取了资源 1,而想请求资源 2,这就形成资源请求等待的环形图;

死锁检测:

jvisualvm,的线程dump;

避免死锁:

  1. 顺序加锁;

  2. 请求锁时限/失败返回

    reentrantLock的tryLock

# 如何设置线程数量?

  • CPU密集型任务(N+1):多出来的一个线程防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响,一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间;
  • IO密集型(2N):线程处理IO的时间段不会占用CPU,这时可以将CPU交出给其它线程使用;
死锁模拟

死锁模拟→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式