TPS:每秒处理的事务数

QPS:每秒处理的查询数

进程

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

线程

一个进程可以有多个线程

并发

两个或多个任务在同一时刻间隔发生

并行

当有多个cpu核心时多个任务同时运行

线程的启动过程

当调用start方法启动线程时 jvm层面创建一个线程JVM具有跨平台特性 她会根据当前操作系统的类型调用相关指令来创建线程并启动
线程启动后不会立刻执行 而是等到操作系统层面的cpu 调度算法 把当前线程分配给某个cpu执行 当线程被分配之后 会回调线程的run方法

线程运行状态

NEW 新建状态

RUNNABLE 运行状态 调用 start方法

BLOCKED 阻塞状态 没抢到锁的线程

TIMED_WAITING 超时等待 sleep(1000) 过了一段时间会唤醒

WAITING wait方法

TERMINATED 终止状态 run方法执行完毕

中断处于运行状态的线程

  1. Thread.currentThread().isInterrupted() 默认是 false
  2. @Override
  3. public void run() {
  4. int i=0;
  5. while (!Thread.currentThread().isInterrupted()) {
  6. i++;
  7. }
  8. System.out.println("已中断"+i);
  9. }
  10. Thread thread = new Thread(new TestController());
  11. thread.start();
  12. Thread.sleep(100);
  13. System.out.println("线程终端状态"+thread.isInterrupted());
  14. //调用 interrupt 中断 将false 变成true
  15. thread.interrupt();
  16. System.out.println("线程终端状态"+thread.isInterrupted());

中断处于阻塞状态的线程

当调用interrupt方法中断线程的时候会抛出java.lang.InterruptedException: sleep interrupted异常说明会先唤醒处于阻塞状态的线程
在抛出 InterruptedException异常之前会先把线程状态进行复位 中断标记变成false

  1. @Override
  2. public void run() {
  3. while (!Thread.currentThread().isInterrupted()) {
  4. try {
  5. TimeUnit.SECONDS.sleep(500);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. //如果出现异常仍需要调用则
  9. Thread.currentThread();
  10. }
  11. }
  12. System.out.println("线程中断");
  13. }
  14. UserServiceImpl testThread = new UserServiceImpl();
  15. testThread.start();
  16. TimeUnit.SECONDS.sleep(100);
  17. System.out.println("线程状态"+Thread.currentThread().isInterrupted());
  18. testThread.interrupt();
  19. System.out.println("线程状态"+Thread.currentThread().isInterrupted());

interrupt() 原理解析

调用本地方法
jvm中_interrupt 使用了volatile修饰的int类型变量 该变量有两个值 1和0 其中1代表true 0代表 fasle

当线程A调用interrupt()方法的时候 调用native()方法修改jvm中定义的 interrupt 变量 线程B通过调用isInterrupted()方法来获取这个变量值 进而判断当前的中断状态

上下文切换

导致上下文切换的原因

1.多个任务抢占锁资源
2.在线程运行过程中存在I/O阻塞 CPU调度器会切换CPU时间片
3.在线程中通过主动阻塞当前线程的方法释放CPU时间片
4.当前线程执行完成后释放CPU时间片 CPU重新调度

上下文切换的概念

CPU把自己的时间片轮流分配给其他线程

上下文切换的类型

1.进程上下文切换

当前线程的CPU时间片分配给其他进行执行
进行切换分三种情况
1.CPU时间片分配
2.当进程系统资源(如内存)不足时 进程会被挂起
3.当存在优先级更高的进行运行时 当前进程可能会被挂起 cpu 时间片会分配给优先级更高的进行进行
2.线程上下文切换
3.中断上下文切换

2.线程上下文切换

1.cpu本身故障 程序故障
2.I/O中断

如何减少上下文切换

1.减少线程数
2.采用无锁设计解决线程竞争问题
3.采用 cas做自旋操作

守护线程

线程分为

1.用户线程

2.守护线程

守护线程不会影响jvm进程的退出 而守户线程在有任务没有执行完成前 jvm 不会退出 直到用户线程运行完毕

  1. Thread thread = new Thread();
  2. thread.setDaemon(true);

使用场景

1.jvm 垃圾回收器
2.心跳检测

使用注意

如果一个线程时守护线程 那么它创建的子线程默认就是守护线程
thread.setDaemon(true); 必须在start方法启动之前调用

死锁

A线程抢占到的锁 B线程想获取 B线程抢占到的锁A线程想获取 然后谁都获取不到对方的锁 直接造成死锁

产生死锁的条件

  1. 互斥条件:所谓互斥就是进程在某一时间内独占资源。
    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

活锁

任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。

饥饿锁

一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。

产生饥饿锁的条件

  • 高优先级线程吞噬所有的低优先级线程的CPU时间。
    - 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。
    - 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法),因为其他线程总是被持续地获得唤醒。

    排查死锁的方式

    1.jps 命令查看java 进行的pid
    2.通过 jstack 查看线程 dump日志

导致线程不安全的原因

1.原子性
2.有序性
3.可见性

线程调度算法

采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优先级上,如非特别需要,尽量不要用,防止线程饥饿。

sleep、wait、yield、join 区别

Sleep

阻塞线程 不会释放锁 让 cpu执行权给其他线程

wait

进入阻塞状态 并释放锁 只有当其他线程notify 才会唤醒此线程

yield

暂停正在执行的线程 不会释放锁 不会阻塞而是让线程进入就绪状态

join

调用join 的线程等其他线程执行完毕之后在执行