线程和进程
1 进程&线程
1.1 进程
进程本质上是一个运行的程序,是系统分配资源的基本单位,每一个进程都有一个独立的内存地址空间;(包括代码空间,数据空间),每一个进程内存空间,数据空间都是私有的, 不能被其他的进程访问;
也就是说进程与进程之间相互隔离的。
1.2 线程
线程是进程中一个任务,一个进程一个至少应该有一个线程(任务)。为了让 cpu 执行任务更加高效,提出了线程的任务执行方式。
进程中所有的资源都是被线程所共享的,线程是 cpu 分配的基本单位;(线程是 cpu 执 行的基本单元),线程之间操作共享资源来进行通信,线程共享进程的资源比进程通信更加 简单,高效。
1) 上下文切换
现在服务器资源: 多核心的 cpu 资源,操作系统调度时候运行很多的进程,支持远大于 cpu 核心数的线程运行,操作系统如何分配 cpu 资源给多线程的呢?
CPU 采用时间分片的模式将 cpu 的时间片段轮流分配给多个线程执行;分配每一个线程的时 间大概就是几十 ms, 线程在 cpu 时间片执行,如果当前线程没有执行结束,cpu 时间分片已 经结束,此线程就会别挂起,下一个 cpu 时间分片将会分配给下一个线程,上一个线程等待 被唤醒。
问题:线程没有执行完毕,线程别挂起,等待下次被唤醒,继续完成任务!系统是怎么知道 线程之前运行到哪里?从哪里开始执行呢??
CPU : 寄存器 (存储数据状态信息)
程序计数器: 存储 cpu 正在执行的指令,执行下一条指令位置
Cpu 在执行任务时候,都必须依赖寄存器,程序计数器;这些东西就是 cpu 切换的开销;称 之为上下文切换
2)线程的调度
问题: 操作系统如何共享 cpu 时间分片,分配 cpu 执行权限? 线程何时分片到时间分片?
线程分配多长时间??重要的线程分配时间多一些? 次要的线程分配时间少一些??
(1)、抢占式调度
并发线程模式下,所有的线程都会抢占时间分片,获取执行权限。有些线程执行时间长, 造成线程阻塞等待,等待 cpu 资源;
JVM 线程调度:抢占式调度模式
绝大多数的操作系统都采用的是抢占式调度
(2)、协同式调度
一个线程执行完成后主动通知系统切换到另一个线程执行;(同步阻塞),致命缺点: 一个 线程阻塞,导致整个进程阻塞,一个线程异常,导致整个程序崩溃。
有很大的潜在风险
3)、并行,并发
(1)、并发: 一段时间内,多个线程轮流分配时间分片,抢占式执行。这就叫做并发;
(2)、并行:同一时刻,多个线程同时执行;这就叫做并行执行;
2多线程实现方式
1继承 Thread 类,实现多线程
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: zhaokj
* @Date: 2022/01/17/14:11
* @Description:
**/
public class testThread {
public static void main(String[] args) {
System.out.println("线程开始01继承Thread------start");
//创建线程
ThreadA threadA = new ThreadA();
//开启线程
threadA.run();
System.out.println("线程开始01继承Thread-------end");
}
@Slf4j
public static class ThreadA extends Thread{
//线程的执行主体
@Override
public void run(){
log.info("线程开始01继承Thread");
//业务代码
int i = 100 / 3;
log.info("i =" + i + " 线程名字" + this.getName() + " 线程id" + this.getId());
}
}
}
继承Runnable方法实现多线程
2.1普通方法
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: zhaokj
* @Date: 2022/01/17/15:02
* @Description:
**/
public class TestThread02 {
public static void main(String[] args) {
//创建多线程对象
ThreadB threadB = new ThreadB();
//创建多线程对象
Thread thread = new Thread(threadB);
thread.run();
}
@Slf4j
public static class ThreadB implements Runnable{
@Override
public void run() {
log.info("继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("继承Runnable方法实现多线程的结果{}",i);
//不能用this.getName和this.getId的方法获得相关参数
}
}
}
2.2匿名内部类
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: zhaokj
* @Date: 2022/01/17/15:02
* @Description:
**/
@Slf4j
public class TestThread02 {
public static void main(String[] args) {
//普通方法
//创建多线程对象
ThreadB threadB = new ThreadB();
//创建多线程对象
Thread thread = new Thread(threadB);
thread.run();
//匿名内部类的方法开启
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info("---匿名内部类继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("---匿名内部类继承Runnable方法实现多线程的结果{}",i);
//不能用this.getName和this.getId的方法获得相关参数
}
};
Thread thread1 = new Thread(runnable);
thread1.run();
}
@Slf4j
public static class ThreadB implements Runnable{
@Override
public void run() {
log.info("继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("继承Runnable方法实现多线程的结果{}",i);
//不能用this.getName和this.getId的方法获得相关参数
}
}
}
2.3 FunctionalInterface
可以使用lambda表达式的方法创建
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: zhaokj
* @Date: 2022/01/17/15:02
* @Description:
**/
@Slf4j
public class TestThread02 {
public static void main(String[] args) {
//普通方法
//创建多线程对象
ThreadB threadB = new ThreadB();
//创建多线程对象
Thread thread = new Thread(threadB);
thread.run();
//匿名内部类的方法开启
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info("---匿名内部类继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("---匿名内部类继承Runnable方法实现多线程的结果{}",i);
//不能用this.getName和this.getId的方法获得相关参数
}
};
Thread thread1 = new Thread(runnable);
thread1.run();
new Thread(()->{
log.info("---FunctionalInterface继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("---FunctionalInterface继承Runnable方法实现多线程的结果{}",i);
}).run();
}
@Slf4j
public static class ThreadB implements Runnable{
@Override
public void run() {
log.info("继承Runnable方法实现多线程");
int i = 100 / 3;
log.info("继承Runnable方法实现多线程的结果{}",i);
//不能用this.getName和this.getId的方法获得相关参数
}
}
}
3.使用Callable + FutureTask 实现多线程
有回掉方法
jdk1.5 后:添加 callable 接口,实现多线程,相较于 Thread, runnable
接口没有返回值,但是 callable 接口是有返回值。
@FunctionalInterface:支持lambda表达式
* public interface Callable<V> {
* 有泛型,只有一个方法call,call方法就是多线程执行业务主体,
* 方法执行完毕后有返回值,返回值的类型就是制定的泛型类型
* 疑问:多线程的执行必须和thread有关,callable接口和thread有什么关系??
3.1普通方法
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: zhaokj
* @Date: 2022/01/17/16:22
* @Description:
* @FunctionalInterface:支持lambda表达式
* public interface Callable<V> {
* 有泛型,只有一个方法call,call方法就是多线程执行业务主体,
* 方法执行完毕后有返回值,返回值的类型就是制定的泛型类型
* 疑问:多线程的执行必须和thread有关,callable接口和thread有什么关系??
**/
@Slf4j
public class TestThread03 {
public static void main(String[] args) {
System.out.println("------------------start---------------");
ThreadC threadC = new ThreadC();
FutureTask<Integer> futureTask = new FutureTask<>(threadC);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("-----------------end----------------");
}
public static class ThreadC implements Callable<Integer>{
@Override
public Integer call() throws Exception {
log.info("实现callable方法实现多线程");
int i = 100 / 3;
log.info("实现callable方法实现多线程的结果{}",i);
return i;
}
}
}
此时 System.out.println("-----------------end----------------");并不会等待线程执行完毕
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: zhaokj
* @Date: 2022/01/17/16:22
* @Description:
* @FunctionalInterface:支持lambda表达式
* public interface Callable<V> {
* 有泛型,只有一个方法call,call方法就是多线程执行业务主体,
* 方法执行完毕后有返回值,返回值的类型就是制定的泛型类型
* 疑问:多线程的执行必须和thread有关,callable接口和thread有什么关系??
**/
@Slf4j
public class TestThread03 {
public static void main(String[] args) {
System.out.println("------------------start---------------");
ThreadC threadC = new ThreadC();
FutureTask<Integer> futureTask = new FutureTask<>(threadC);
Thread thread = new Thread(futureTask);
thread.start();
try {
//同步阻塞必须等待异步线程执行结束后才能输出end
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("-----------------end----------------");
}
public static class ThreadC implements Callable<Integer>{
@Override
public Integer call() throws Exception {
log.info("实现callable方法实现多线程");
int i = 100 / 3;
log.info("实现callable方法实现多线程的结果{}",i);
return i;
}
}
}
3.2 匿名内部类
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: zhaokj
* @Date: 2022/01/17/17:20
* @Description:
**/
@Slf4j
public class TestThread03_1 {
public static void main(String[] args) {
System.out.println("----------start---------");
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.info("匿名内部类实现callable方法实现多线程");
int i = 100 / 3;
log.info("匿名内部类实现callable方法实现多线程的结果{}", i);
return i;
}
});
Thread thread = new Thread(task);
thread.start();
try {
Integer integer = task.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("----------end---------");
}
}
3.3lambda表达式
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: zhaokj
* @Date: 2022/01/17/20:09
* @Description:
**/
@Slf4j
public class TestThread03_2 {
public static void main(String[] args) {
System.out.println("---------------start------------------");
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
log.info("匿名内部类实现callable方法实现多线程");
int i = 100 / 3;
log.info("匿名内部类实现callable方法实现多线程的结果{}", i);
return i;
});
Thread thread = new Thread(futureTask);
thread.start();
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("---------------end------------------");
}
}
3.线程池
项目开发中,不会使用上面 3 种线程的实现方式,因为上面 3 种线程实现方式无法控制线程,可能会造成系统资源耗尽,浪费系统资源,造成系统的性能下降;
在企业业务开发中,必须使用线程池的方式,构建多线程;让线程充分利用,降低系统的资源的消耗。
问题: 为什么要使用线程池???(池化技术)–从实践出发------0 基础学架构------VIP 开课吧-秒杀项目实战
我们需要执行一个 java 任务,可以直接 new Thread 来运行任务,
线程从创建到消耗经历那些过程??
1、创建 java 线程实例,线程是一个对象实例,堆内存中分配内存(创建线程需要消耗时间和内存)
2、执行 start 方式启动线程,操作系统为 Java 线程创建对应的内核线程,线程处于就绪状态(内核线程是操作系统的资源,创建需要时间和内存)
3、线程被操作系统 cpu 调度器选中后,线程开始执行(run 方法开始运行)
4、JVM 开始为线程创建线程私有资源:JVM 虚拟机栈*程序计数器(需要时间和内存)
5、线程运行过程中,cpu 上下文切换 (消耗时间,频繁切换,影响性能)
6、线程运行完毕,Java 线程被垃圾回收器回收 (销毁线程内存需要时间)
从线程执行的流程来看:
1、线程不仅是 java 对象,更是操作系的资源(创建线程,消耗线程都需要时间)
2、Java 线程的创建和运行都需要内存空间(线程数量太多,消耗很多内存)
3、cpu 上下文切换(线程数量一大,cpu 频繁切换)
线程池优势:
1、降低系统的资源的消耗
2、提供系统的响应速度
3、方便管理(线程复用,控制最大并发数,管理线程)
事先准备好一些资源,用人要用(业务系统要是有线程),就来我这里拿(线程池中获取),
用完之后不能销毁,必须还给我(线程池线程可复用性);
线程池的创建
创建单线程池
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: zhaokj
* @Date: 2022/01/17/23:10
* @Description:
**/
@Slf4j
public class ThreadPool {
/**
*
* @param args
*/
public static void main(String[] args) {
//创建线程池对象,创建单个线程池对象
ExecutorService executorService = Executors.newSingleThreadExecutor();
//线程执行
try {
executorService.execute(()->{
log.info("ExcutorS创建线程的方式实现多线程.......");
int i = 100 / 3;
log.info("ExcutorS创建线程的方式实现多线程结果{}",i);
});
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
创建指定线程数线程池
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: zhaokj
* @Date: 2022/01/17/23:10
* @Description:
**/
@Slf4j
public class ThreadPool {
/**
*
* @param args
*/
public static void main(String[] args) {
//创建线程池对象,创建指定个数线程池对象,此处为2个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//线程执行
try {
for (int j = 1; j<10;j++){
executorService.execute(()->{
log.info("ExcutorS创建线程的方式实现多线程.......");
int i = 100 / 3;
log.info("ExcutorS创建线程的方式实现多线程结果{}",i);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
创建一个按照计划执行的线程池
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: zhaokj
* @Date: 2022/01/17/23:10
* @Description:
**/
@Slf4j
public class ThreadPool2 {
/**
*
* @param args
*/
public static void main(String[] args) {
//创建线程池对象,创建一个按照计划执行的线程池
ExecutorService executorService = Executors.newScheduledThreadPool(2);
//线程执行
try {
for (int j = 1; j<10;j++){
executorService.execute(()->{
log.info("ExcutorS创建线程的方式实现多线程.......");
int i = 100 / 3;
log.info("ExcutorS创建线程的方式实现多线程结果{}",i);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
创建自动增长的线程池
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: zhaokj
* @Date: 2022/01/17/23:10
* @Description:
**/
@Slf4j
public class ThreadPool3 {
/**
*
* @param args
*/
public static void main(String[] args) {
//创建线程池对象,创建自动增长的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//线程执行
try {
for (int j = 1; j<10;j++){
executorService.execute(()->{
log.info("ExcutorS创建线程的方式实现多线程.......");
int i = 100 / 3;
log.info("ExcutorS创建线程的方式实现多线程结果{}",i);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
使用的是execute方式创建的线程池,没有返回值,如果需要返回值就需要commit
不建议使用execute的方式创建线程池
原因是以上的线程池创建的方式,当线程量一大后,可能造成无限制创
建线程,从而导致内存被占满,线程量大导致性能严重下降,甚至 OOM;
参数解析:
1、corePoolSize: 线程池核心线程数,初始化线程池时候,会创建 核心线程等待状态,核心线程不会被销毁,提供线程的复用;
2*、*maximumPoolSize: 最大线程数;核心线程用完了,必须新建线程 执行任务,但是新建的线程不能超过最大线程数;
3*、keepAliveTime:线程存活时间,除了核心线程以为* (maximumPoolSize- corePoolSize)的线程存活时间;当线程处于空闲状态,
他可以活多久;
4*、*unit: 存活时间单位
5*、*workQueue:任务阻塞队列,任务可能会很多,线程就那么几个,因此可以把多余的任务放入队列进行缓冲,队列采用 FIFO 的,等待线程空闲, 再从队列取出任务执行;
6.threadFactory: 线程工厂,默认使用 defaultthreadFactory, 用来创建线程的,一般使用默认即可;
7.RejectedExecutionHandler: 线程池拒绝策略
正确的方式:
使用ThreadPoolExecute创建线程池
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
Executors.defaultThreadFactory()
threadFactory: 线程工厂,默认使用 defaultthreadFactory, 用来创建线
程的,一般使用默认即可;
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
RejectedExecutionHandler: 线程池拒绝策略
四种拒绝策略:
(1)、AbortPolicy : 新任务直接被拒绝,抛出异常: RejectedExecutionException;
(2)、DisCardPolicy: 队列满了,新任务忽略不执行,直接丢弃,不会抛出异常
(3)、DisCardOldestPolicy: 队列满了,新任务尝试和等待最久的线程竞争,也不会抛出异常;抛弃任务队列中等待最久任务,新任务直接添加到队列中
(4)、CallerRunPolicy: 新任务来临后,直接使用调用者所在线程执行任务即可
使用ThreadPoolExecute创建线程,拒绝策略使用RejectedExecutionException
可以通过DefaultThreadFactory来命名线程池
package com.kkb.cubemall.juc;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @Author: zhaokj
* @Date: 2022/01/17/23:10
* @Description:
**/
@Slf4j
public class ThreadPoolExecuteDemo {
/**
*
* @param args
*/
public static void main(String[] args) {
//创建线程池对象,创建自动增长的线程池
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new java.util.concurrent.ThreadPoolExecutor.AbortPolicy());
//线程执行
try {
for (int j = 1; j<20;j++){
executorService.execute(()->{
log.info("ExcutorS创建线程的方式实现多线程.......");
int i = 100 / 3;
log.info("ExcutorS创建线程的方式实现多线程结果{}",i);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
设置线程系线程数的数量
设置线程系线程数的数量: 根据业务类型进行设置(cpu 密集型,Io 密集型)
如果 CPU 密集型任务(所有任务都在内存中执行:没有磁盘的读写): 建议线程池最大数量设置为 N(cpu 核心数量)+1
如果 IO 密集型任务(大量磁盘读写任务):如果有 IO 操作,cpu 此时处于空闲状态, 最大线程数应该设置:2N+1
最大线程数设置公式:
最大线程数 = (任务执行时间 / 任务 cpu 时间)*N
队列使用
在 Java 并发编程中,使用线程池,还必须使用任务队列,让任务在队列中进行缓冲;可以
线程执行防洪泄流的效果,提升线程池处理任务能力;
java.util.concurrent 接口 BlockingQueue
-
类型参数:
E
- 在此 collection 中保持的元素类型 -
所有超级接口:
-
所有已知子接口:
-
所有已知实现类:
ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue
首先在线程池中使用的都是阻塞队列 存和放都会阻塞
基于 Java 一些队列实现特性:
1、ArrayBlockingQueue : 基于数组实现的有界的阻塞队列 (有界的队列)
2、LinkedBlockingQueue: 基于链表实现的有界阻塞队列
3、PriorityBlockingQueue: 支持按照优先级排序的无界的阻塞的队列
4、DelayQueue: 优先级实现的无界阻塞队列
5、SynchronousQueue : 只存储一个元素,如果这个元素没有被消费,不能在向里面存储元素
6、LinkedTransferQueue: 基于链表实现的无界的阻塞队列
7、LinkedBlockingDeque: 基于链表实现的双向的无界阻塞的队列
如何选择一个合适的队列:建议必须使用有界队列
有界队列能增加系统的稳定性,根据需求设置大一些(可控设置); 如果设置为无界队列,遇到不可控的因素,可能会导致队列中的任务越来越多,出现 OOM,撑爆整个系统;
此时如果还想继续的提升性能就要开启下一个话题-----异步计算
**4 **异步计算
3.1 什么是异步
异步调用实现一个不需要被等等的方法的返回值;让调用者继续执行(异步执行);在java 中,简单的讲就是开启另一个线程完成程序计算,使得调用者继续执行,不需要等等计算的结果,但是调用者任然需要获取线程的计算结果(不需要同步阻塞等待)。
3.2 Future
Future 也是一个异步计算结果返回接口,目的获取返回值结果。但是 future 在获取返回值结构的时候,方法必须同步阻塞等待返回值结果。
Get : 获取结果(等待,阻塞)
Get(timeout) : 获取结果,指定等待时间
Cancel : 取消当前任务**–从实践出发------0 基础学架构------VIP 开课吧-秒杀项目实战**
isDone : 判断任务是否已经完成 (轮询)
futrure 对于结果获取不是很方便,只能通过同步阻塞的方式获取结果,或者是轮询的方式获取到结果;阻塞的方式获取返回值结果与异步的思想想违背,轮询方式又很占用 cpu 资源,也不能及时得到我们结果。
为了简化异步编程的复杂性,提升我们的编程效率就要用到了异步编排的结果
3.3异步编排
CompletableFuture 可以帮助我们简化异步编程复杂性,提供了函数式编程的能力,可以通 过回调函数的方式处理计算结果。
CompletableFuture 具有Future的特性,还实现了CompletionStage接口,具备CompletionStage接口的特性: 串行执行,并行执行,聚合(AND 聚合,OR 聚合)
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
1)串行关系执行
串行关系执行: then – 然后,也就是表示下一步,所以通常是一个串行的关系体现,then
后面的单词(比如 run/apply/accept)就是函数是接口中抽象方法名称;
串行关系执行: 利用上一步的执行结果,去进行下一步任务执行,任务执行具有先后顺序,
因此把这种操作叫做串行关系。
Run
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
Apply
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);
Accept
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);
Compose
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor);
2)聚合 AND
Combine…… with …… 和 both…… and …… 都是要求两者都必须满足,也就是
and 且的关系。
public <U,V> CompletionStage<V> thenCombine (CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync (CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync (CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor);
3.4 异步开启
1)RunAsync:没有使用自定义线程池,没有返回值,默认使用的是ForkJoinPool线程池
@Slf4j
public class AsycFutureDemo {
public static void main(String[] args) {
log.info("主线程开始---------->start");
//runAsync:实现异步编排,没有返回值
//public static ComplateFuture<Void> runAsync(Runnable runnable);
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() ->{
//返回值的泛型为void,说明不关注返回值
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
});
try {
voidCompletableFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
log.info("主线程开始---------->end");
}
}
2)RunAsync:使用自定义线程池,没有返回值,默认使用的是Thread1线程池
@Slf4j
public class AsycFutureDemo {
public static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new java.util.concurrent.ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
//创建线程池对象,创建自动增长的线程池
log.info("主线程开始---------->start");
//runAsync:实现异步编排,没有返回值
//public static ComplateFuture<Void> runAsync(Runnable runnable);
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() ->{
//返回值的泛型为void,说明不关注返回值
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
},executorService);
try {
voidCompletableFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
log.info("主线程开始---------->end");
}
}
3)supplyAsync
supplyAsync:实现异步编排,没有返回值
public static void main(String[] args) {
//创建线程池对象,创建自动增长的线程池
log.info("主线程开始---------->start");
//supplyAsync:实现异步编排,没有返回值
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
CompletableFuture<Integer> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
//返回值的泛型为void,说明不关注返回值
log.info("子线程开始---------->start");
int i = 10 / 3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(), i);
return i;
});
try {
Integer integer = voidCompletableFuture.get();
log.info("supplyAsync 运行结果{}",integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
log.info("主线程开始---------->end");
}
4)thenRun
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenrun没有返回值,也不关心上一步的返回值,只和上一步的执行顺序有关
//public ComplationStage<Void> thenRUN(Runnable action);
log.info("主线程开始---------->start");
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).thenRun(() -> {
log.info("thenrun开始运行---------->start");
});
voidCompletableFuture.get();
log.info("主线程结束---------->end");
}
thenrun没有返回值,也不关心上一步的返回值,只和上一步的执行顺序有关
5)thenRunAsync
thenRunAsync没有返回值,也不关心上一步的返回值,只和上一步的执行顺序有关
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenRunAsync没有返回值,也不关心上一步的返回值,只和上一步的执行顺序有关
//public ComplationStage<Void> thenRunAsync(Runnable action);
log.info("主线程开始---------->start");
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).thenRunAsync(() -> {
log.info("thenRunAsync开始运行---------->start");
});
voidCompletableFuture.get();
log.info("主线程结束---------->end");
}
6)thenApply
public ComplationStage thenApply(Function<?super T,?extends U> fn);
T 就是返回值类型,U就是参数类型
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenApply 没有返回值,返回值类型是U,跟上一步执行结果有关系,上一步执行结果会被当成参数传递给下一步,参数类型为T
//public ComplationStage<Void> thenApply(Function<?super T,?extends U> fn);
log.info("主线程开始---------->start");
CompletableFuture<Long> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).thenApply((t) -> {
log.info("thenRunAsync开始运行---------->start,t的值为:{}",t);
long res = t * 8;
log.info("thenApply异步计算的结果: {}",res);
return res;
});
Long aLong = voidCompletableFuture.get();
log.info("thenApply的结果:{}",aLong);
log.info("主线程结束---------->end");
}
7)thenAccept
public static ComplateFuture supplyAsync(Supplier supplier);
thenAccept:没有返回值,跟上一步执行结果有关系,上一步执行结果会被下一步消费,参数类型为T
public CompletionStage thenAccept(Comsumer<? super T> action);
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenAccept:没有返回值,跟上一步执行结果有关系,上一步执行结果会被下一步消费,参数类型为T
//2、public CompletionStage<Void> thenAccept(Comsumer<? super T> action);
log.info("主线程开始---------->start");
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).thenAccept((t) -> {
log.info("thenRunAsync开始运行---------->start,t的值为:{}",t);
long res = t * 8;
log.info("thenApply异步计算的结果: {}",res); //没有返回值
});
voidCompletableFuture.get();
log.info("主线程结束---------->end");
}
8)thenCombine
public static ComplateFuture supplyAsync(Supplier supplier);
thenCombine
1。有返回值
2。thenCombine会把两个ComplateionStage的任务都执行完毕后,把两个任务的结果一块交给thenCombine去处理
public<U,V> CompletionStage thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V>)
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenCombine
//1。有返回值
//2。thenCombine会把两个ComplateionStage的任务都执行完毕后,把两个任务的结果一块交给thenCombine去处理
//public<U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V>)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /4;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
CompletableFuture<Integer> integerCompletableFuture = voidCompletableFuture.thenCombine(voidCompletableFuture1, (u, t) -> {
log.info("u的值{}:", u);
log.info("t的值{}:", t);
return u + t;
});
Integer integer = integerCompletableFuture.get();
System.out.println(integer);
log.info("主线程结束======end");
}
9)thenAcceptBoth
public static ComplateFuture supplyAsync(Supplier supplier);
thenAcceptBoth
1。没有返回值
2。thenAcceptBoth会把两个ComplateionStage的任务都执行完毕后,把两个任务的结果一块交给thenAcceptBoth去处理
public static void main(String[] args) {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenAcceptBoth
// 1。没有返回值
//2。thenAcceptBoth会把两个ComplateionStage的任务都执行完毕后,把两个任务的结果一块交给thenAcceptBoth去处理
//public<U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiFunction<? super T,? super U,action)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /4;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
CompletableFuture<Integer> voidCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
//注意没有返回值
CompletableFuture<Void> integerCompletableFuture = voidCompletableFuture1.thenAcceptBoth(voidCompletableFuture2, (t,u) -> {
log.info("u的值{}:", u);
log.info("t的值{}:", t);
});
integerCompletableFuture.get();
log.info("主线程结束======end");
}
10)runAfterBoth
public static void main(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenAfterBoth
// 1。没有返回值
//2。thenAcceptBoth会把两个ComplateionStage的任务都执行完毕后,才会执行下一步操作
//public<U> CompletionStage<Void> runAfterboth(CompletionStage<?> other,Runnable action)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /4;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
CompletableFuture<Integer> voidCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
//注意没有返回值
CompletableFuture<Void> integerCompletableFuture = voidCompletableFuture1.runAfterBoth(voidCompletableFuture2, () -> {
log.info("runAfterBoth开始---------->start");
});
integerCompletableFuture.get();
log.info("主线程结束======end");
}
3) OR
关键次 either
1)applyToEither
public static void runAfterBoth(String[] args) throws ExecutionException, InterruptedException {
//public static ComplateFuture<Void> supplyAsync(Supplier<U> supplier);
//thenAfterBoth
// 1。没有返回值
//2。thenAcceptBoth会把两个ComplateionStage的任务都执行完毕后,才会执行下一步操作
//public<U> Complete<Void> runAfterboth(CompletionStage<?> other,Runnable action)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /4;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
CompletableFuture<Integer> voidCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /3;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
});
//注意没有返回值
//利用applyToEither做合并操作,or的关系
CompletableFuture<Void> integerCompletableFuture = voidCompletableFuture1.runAfterBoth(voidCompletableFuture2, () -> {
log.info("applyToEither子线程开始,f1和f2执行的快的一个是:---------->{}",v);
});
integerCompletableFuture.get();
log.info("主线程结束======end");
}
异常处理
exceptionally、whenComplete、handle
1> exceptionally
public static void main(String[] args) throws ExecutionException, InterruptedException {
// public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /4;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).exceptionally((t)->{
log.info("业务失败:{1}",t.getMessage());
return null;
});
voidCompletableFuture1.get();
log.info("主线程end..................");
}
不产生异常正常执行业务逻辑
捕获业务逻辑的异常
2)whenComplete
public static void main(String[] args) throws ExecutionException, InterruptedException {
// public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /0;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).whenComplete((t,u)->{
log.info("执行结果:{}",t);
if (u!=null) {
log.info("业务失败:{}", u.getMessage());
}
});
voidCompletableFuture1.get();
log.info("主线程end..................");
}
3)handle
public static void main(String[] args) throws ExecutionException, InterruptedException {
// public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
//还可以处理执行结果
//相当于 try{}finally{} : 对上一步执行结果进行处理,还可以处理异常任务
log.info("主线程开始---------->start");
CompletableFuture<Integer> voidCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("子线程开始---------->start");
int i = 10 /0;
log.info("子线程开始---------->end 线程名称:{} 执行结果{}", Thread.currentThread().getName(),i);
log.info("子线程future开始运行---------->start");
return i;
}).handle((t,u)->{
log.info("执行结果:{}",t);
int res = -1;
if (u!=null) {
log.info("业务失败:{}", u.getMessage());
}else {
res = t * 5;
}
return res;
});
Integer integer = voidCompletableFuture1.get();
log.info("最终的执行结果:{}",integer);
log.info("主线程end..................");
}
有异常被捕获
没有异常,可以对执行结果进行处理