TPS:每秒处理的事务数
QPS:每秒处理的查询数
进程
进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元
线程
并发
并行
当有多个cpu核心时多个任务同时运行
线程的启动过程
当调用start方法启动线程时 jvm层面创建一个线程JVM具有跨平台特性 她会根据当前操作系统的类型调用相关指令来创建线程并启动
线程启动后不会立刻执行 而是等到操作系统层面的cpu 调度算法 把当前线程分配给某个cpu执行 当线程被分配之后 会回调线程的run方法
线程运行状态
NEW 新建状态
RUNNABLE 运行状态 调用 start方法
BLOCKED 阻塞状态 没抢到锁的线程
TIMED_WAITING 超时等待 sleep(1000) 过了一段时间会唤醒
WAITING wait方法
TERMINATED 终止状态 run方法执行完毕
中断处于运行状态的线程
Thread.currentThread().isInterrupted() 默认是 false@Overridepublic void run() {int i=0;while (!Thread.currentThread().isInterrupted()) {i++;}System.out.println("已中断"+i);}Thread thread = new Thread(new TestController());thread.start();Thread.sleep(100);System.out.println("线程终端状态"+thread.isInterrupted());//调用 interrupt 中断 将false 变成truethread.interrupt();System.out.println("线程终端状态"+thread.isInterrupted());
中断处于阻塞状态的线程
当调用interrupt方法中断线程的时候会抛出java.lang.InterruptedException: sleep interrupted异常说明会先唤醒处于阻塞状态的线程
在抛出 InterruptedException异常之前会先把线程状态进行复位 中断标记变成false
@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {TimeUnit.SECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();//如果出现异常仍需要调用则Thread.currentThread();}}System.out.println("线程中断");}UserServiceImpl testThread = new UserServiceImpl();testThread.start();TimeUnit.SECONDS.sleep(100);System.out.println("线程状态"+Thread.currentThread().isInterrupted());testThread.interrupt();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 不会退出 直到用户线程运行完毕
Thread thread = new Thread();thread.setDaemon(true);
使用场景
使用注意
如果一个线程时守护线程 那么它创建的子线程默认就是守护线程
thread.setDaemon(true); 必须在start方法启动之前调用
死锁
A线程抢占到的锁 B线程想获取 B线程抢占到的锁A线程想获取 然后谁都获取不到对方的锁 直接造成死锁
产生死锁的条件
- 互斥条件:所谓互斥就是进程在某一时间内独占资源。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
活锁
任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。
饥饿锁
一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。
产生饥饿锁的条件
- 高优先级线程吞噬所有的低优先级线程的CPU时间。
- 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。
- 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法),因为其他线程总是被持续地获得唤醒。排查死锁的方式
1.jps 命令查看java 进行的pid
2.通过 jstack查看线程 dump日志
导致线程不安全的原因
1.原子性
2.有序性
3.可见性
线程调度算法
采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优先级上,如非特别需要,尽量不要用,防止线程饥饿。
sleep、wait、yield、join 区别
Sleep
wait
进入阻塞状态 并释放锁 只有当其他线程notify 才会唤醒此线程
yield
暂停正在执行的线程 不会释放锁 不会阻塞而是让线程进入就绪状态
join
调用join 的线程等其他线程执行完毕之后在执行
