程序性能的指标:降低延迟,提高吞吐量。延迟指的是发出请求到收到响应这个过程的时间;延迟越短,意味着程序执行得越快,性能也就越好。 吞吐量指的是在单位时间内能处理请求的数量;吞吐量越大,意味着程序能处理的请求越多,性能也就越好。这两个指标内部有一定的联系(同等条件下,延迟越短,吞吐量越大),但是由于它们隶属不同的维度(一个是时间维度,一个是空间维度),并不能互相转换。  有两个方向,第一个是优化算法,第二个是将提高硬件的性能。硬件性能主要是IO,CPU。所以要提升IO的利用率和CPU的利用率。这里就引出了多线程。

 

线程数该如何设定呢?

首先确定当前任务是CPU密集型任务还是IO密集型任务。对于 CPU 密集型的计算场景线程的数量一般会设置为“CPU 核数 +1”。当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证 CPU 的利用率。

IO密集型任务的话用如下公式: 最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

 

通用的线程生命周期: 初始状态,可运行状态,运行状态,阻塞状态,休眠状态,终止状态。

springboot 吞吐量只有250 java吞吐量什么意思_springboot 吞吐量只有250

初始状态:线程已经创建,但是还未分配CPU执行。

可运行状态:线程可以被分配CPU了。

运行状态:有空闲的CPU时,操作系统会分配CPU给线程,线程状态变成运行状态。

阻塞状态:互斥锁。临界资源被其他线程占用了,当前线程等待其他线程释放锁后才接触阻塞状态。

休眠状态:运行状态的线程调用了一个阻塞的API,或者等待某个条件,那么线程就会进入阻塞休眠状态。

终止状态:线程执行完,或者出现异常会进入终止状态。进入终止状态就意味着线程生命周期结束。

 

RUNNABLE 与 WAITING 的状态转换

第一种场景,获得 synchronized 隐式锁的线程,调用无参数的 Object.wait() 方法。

第二种场景,调用无参数的 Thread.join() 方法。例如有一个线程对象 thread A,当调用 A.join() 的时候,执行这条语句的线程会等待 thread A 执行完,而等待中的这个线程,其状态会从 RUNNABLE 转换到 WAITING。当线程 thread A 执行完,原来等待它的线程又会从 WAITING 状态转换到 RUNNABLE。

从 RUNNABLE 到 TERMINATED 状态

线程执行完 run() 方法后,会自动转换到 TERMINATED 状态,当然如果执行 run() 方法的时候异常抛出,也会导致线程终止。也可以用thread的stop方法去结束线程,不过这个方法不建议用。

stop() 和 interrupt() 方法的主要区别是:抛出stop方法会真正的杀死线程。如果线程持久的是重入锁,被stop的线程就再也不会去调用重入锁的unlock方法去释放锁。interrupt() 方法仅仅是通知线程,线程有机会执行一些后续操作。

 

局部变量会不会发生线程安全问题呢?

  首先追溯方法是如何被调用的,CPU是如何找到调用方法的参数和返回地址的。答案是通过CPU的堆栈寄存器。CPU支持栈结构,栈是先入后出。每个方法在调用栈里都要自己的独立空间,被称为栈帧,每个栈帧都有对应方法需要的参数和返回地址。当调用方法时,会创建新的栈帧,并压入调用栈;当方法返回时,对应的栈帧就会被自动弹出。也就是说,栈帧和方法是同生共死的

局部变量的作用域是方法内部,也就是方法执行完了,局部变量也就没用了。局部变量和方法是同生共死的。此时你应该会想到调用栈的栈帧,调用栈的栈帧就是和方法同生共死的,所以局部变量放到调用栈里那儿是相当的合理。事实上,局部变量就是放到了调用栈里。一个变量想要跨越方法的边界,就要被创建在堆里。

每个线程都有自己独立的调用栈。局部变量保存在线程自己的调用栈里不会共享。所以没有共享,就没有线程安全问题。也被称为线程封闭。

 

这里加一个问题: 递归调用栈太深的话会导致栈溢出,这是什么原因呢?

局部每次方法调用的时候都在栈上创建一个栈帧,方法调用结束才会弹出该栈帧,但是栈的大小是有限的,递归调用多次创建了很多栈帧,但是之前的方法栈帧还未执行完,栈帧还未出栈又一直添加栈帧,导致栈溢出。