1、并发中的一些概念。
qps : 对于单个接口来说,每秒钟能够处理完成的请求次数,注意是处理完成的请求次数,发出请求到服务器处理完成功返回结果。
tps : 当一次请求包含多个请求的时候,注意是一次请求包含有多个请求,比如请求一个index页面,页面中的数据需要请求多个接
口,这个时候当所有的请求都收到响应后就表示一个tps,多个qps。
rt : 响应时间。
并发量 :系统能够同时处理的请求数或者事务数。
系统吞吐量 :系统吞吐量通常由qps(tps)、并发数量两个因素决定。每套系统这两个值都有极限值,只要有一项到达了极限值,系
统的吞吐量就上不去了,因为如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换频
繁、内存消耗等导致系统性能下降。
计算关系:
qps = 并发量/平均响应时间
2、什么是并发、并行?
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上行。
所以并发是指在一段时间内宏观上多个程序(或多个线程)同时运行。
并行:并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互抢
占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。所以并行指的是同一个时刻,多个任务确实真的在同时行。
并发并行的区别:
1、并发,指的是多个事情,在同一时间段内同时发生了,而并行,指的是多个事情,在同一时间点上同时发生了。
2、并发的多个任务之间是互相抢占资源的,而并行的多个任务之间是不互相抢占资源的。
3、只有在多CPU的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。
3、什么是线程?
线程是cpu的最小调度单元,是用来执行我们的代码编译成的计算机指令的。
4、什么是上下文切换?
当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以 再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。
5、java中使用多线程的方式:
方式1:继承Thread类,重写run方法。
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("doSomething");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
方式2:实现Runnable接口,重写run方法。可以做到任务跟线程解耦。
public class MyRunable implements Runnable {
@Override
public void run() {
System.out.println("doSomething");
}
public static void main(String[] args) throws InterruptedException {
MyRunable task = new MyRunable();
Thread t = new Thread(task);
t.start();
}
}
方式3:Callable<T>/Future<T>,实现Callable接口,重新call方法,然后使用Future来获取任务执行的返回结果,就是回调。
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Success";
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {
//使用的方式1--使用FutureTask
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask(myCallable);
futureTask.run();
String result = futureTask.get();
System.out.println(result);
System.out.println("================================");
//使用方式2--使用线程池提交
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> submit = executorService.submit(myCallable);
System.out.println(submit.get());
}
}
6、线程的生命周期:
Java中线程的生命周期:
7、线程的启动:
new Thread().start();原理:调用一个本地 native 方法 start0(),为什么要使用native方法呢?是因为让jvm去调用os操作系统去创建一条线程,然后启动线程。
start() 与run方法的区别:
new Thread().start() 表示使用当前线程执行任务。
new Thread().run() 表示调用使用主线程调用当前线程实例的run方法,并不是使用创建的线程来执行任务。
8、线程的终止:
Thread t = new Thread();
方式1、t.stop()方法:不建议使用,强制终止当前线程,可能会带来数据不完整的问题,比较暴力。
方式2、t.interrupt()发送终止通知:需要配合中断标记boolean isInterrupted = t.isInterrupted()使用默认值是false,当调用 t.interrupt()会将此值设置为true。
案例如下:
案例:使用线程的isInterrupted标记来做逻辑控制来实现线程的终止。
public class InterruptDemo0 implements Runnable {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("runing ...");
}
System.out.println("stop ...");
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new InterruptDemo0(),"thread001");
t.start();
//输出t线程是否为中断,默认为false,其实就是线程维护了一个中断标记,此标
记默认为false
System.out.println(t.isInterrupted());
//保证t线程执行到睡眠的时候。
Thread.sleep(1000);
//让t线程处于中断状态,就是将线程维护的中断标记设置为true
t.interrupt();
}
}
#1 中断复位:在调用线程t.interrupt()方法的时候,如果当前线程处于等待状态中,
比如调如下方法都会让线程处于WAITING或TIME_WAITING状态中。
Thread.sleep(100000);
InterruptDemo1.class.wait();
Thread.currentThread().join(); 等需要处理InterruptedException的方法。
在调用这些方法到时候都要捕获InterruptedException,在捕获这个异常的时候会触发线程的复位,也就是会将当 前线程的中断标记恢复到默认的false值。那么问题来了,为什么要复位呢?说白了就是把后续的决定权交到开发者手 上,开发者可以在catch里面添加处理,如果开发者就要终止此线程那就再次调Thread.currentThread().interrupt()线 程的终止方法,这个时候就能到达终止线程的目的。如果开发者不想终止线程那就什么都不做。
举个例子来形容这个问题:比如你在睡午觉,有个人突然叫醒你,是继续睡还是起来干活完全由你决定,如果你决定 要起来干活了,你就中断当前的睡觉。
#2 手动复位:我们可以手动调用Thread.interrupted()静态方法来复位当前线程,也就是手动手动将当前线程的中断标记设置 为默认的false值。
复位案例:
public class InterruptDemo1 implements Runnable {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
try {
//当前线程沉睡200秒
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {//捕获这个异常的时候会触发线程的复位,也就是会将当前线程的中断标记 恢复到默认的false值。
//二次中断以终止当前线程。
Thread.currentThread().interrupt();
}
}
System.out.println("线程结束。。。");
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new InterruptDemo1(),"thread001");
t.start();
//输出t线程是否为中断,默认为false,其实就是线程维护了一个中断标记,此标记默认为false
System.out.println(t.isInterrupted());
//保证t线程执行到睡眠的时候。
Thread.sleep(1000);
//让t线程处于中断状态,就是将线程维护的中断标记设置为true
t.interrupt();
//Thread.interrupted();//手动复位当前线程。
}
}
#3 interrupt()方法原理:调用线程的interrupt()方法最终也会调用到一个native方法,其实就是使用jvm层面来做处理,jvm是使用c++来编写的,线程的中断标记就是在jvm层面维护的。