特点:无序,去重
查询去重,只需要设置cunt的值为正数就能查询结果
SRANDMEMBER kk 5
查询所有重复和不重复,只需要设置cunt的值为负数就能查询结果
SRANDMEMBER kk -2
数据库查询快:屏蔽全量IO,数据分治,Key,Value形式,内存
范式:数据存一张表的时候,有些数据会产生冗余,相同的部分,把它拆分成表查询的就不会产生冗余 ,垂直切割
热数据存内存,全量存磁盘,非关系数据
HDFS分布式文件系统
缓存中间件:分布式存储消息转发,数据存储在磁盘,去数据先到进程到内核再到磁盘,中间内存内存映射到数据文件,直接读取,append追加,断文件存储,读取数据给出偏移量到内核零拷贝里面读取,
面向切面编程,通过jdk或者cglib动态代理实现目标对象的接口方法
Spirng的AOP的动态代理实现机制有两种,分别是:
1)JDK动态代理:
具体实现原理:
1、通过实现InvocationHandlet接口创建自己的调用处理器
2、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
3、通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,
Spring通过java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
两者对比:
JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制)
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
那么如何选择的使用代理机制了?
通过配置Spring的中aop:config标签来显示的指定使用动态代理机制 proxy-target-class=true表示使用CGLib代理,如果为false就是默认使用JDK动态代理
Pointcut
:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution
方式和annotation
方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
Advice
:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
Aspect
:切面,即Pointcut
和Advice
。
Joint point
:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
Weaving
:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。
简要解释:
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,
然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
详细解释:
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
1.概念
序列化:把Java对象转换为字节序列的过程。 反序列化:把字节序列恢复为Java对象的过程。
2.用途
对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 2) 在网络上传送对象的字节序列。
小结:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。
private String a = "1";
private StringBuffer b = new StringBuffer();
private StringBuilder c = new StringBuilder("54154");
private Map<String,Object> map = new HashMap<>();//无序,可以存储null值
private Map<String,Object> map6 = new ConcurrentHashMap<>();//无序,可以存储null值,有锁能同步
private Map<String,Object> map2 = new LinkedHashMap<>();//链表有序,
private Map<String,Object> map4 = new Hashtable<>();//不能存null值,线程安全同步
private Map<String,Object> map5 = new TreeMap<>();//树有序
private List<String> list = new ArrayList<>();//数组结构,查询块,增删慢private static final Object[] EMPTY_ELEMENTDATA = {};
private List<String> list1 = new Vector<>();//数组结构,查询慢,责备增删快,有synchronized锁
private List<String> list2 = new LinkedList<>();//链表结构,查询慢,增删快。
private Set<String> set1 = new HashSet<>();//hash算法无序,
private Set<String> set2 = new TreeSet<>();//红黑树有序升序
private Set<String> set3 = new LinkedHashSet<>();//实际数据查询速度而定
session存在服务器数据,重要数据存放
cookie存在客户端数据,次要数据存放,不能超过4k
jwt
M(model)模型 javaBean
V(view)视图 html
C(controller)控制器 servlet
流程
(1)用户发送请求至前端控制器DispatcherServlet; (2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle; (3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet; (4)DispatcherServlet 调用 HandlerAdapter处理器适配器; (5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器); (6)Handler执行完成返回ModelAndView; (7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; (8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; (9)ViewResolver解析后返回具体View; (10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) (11)DispatcherServlet响应用户。
优点
(1)可以支持各种视图技术,而不仅仅局限于JSP;
(2)与Spring框架集成(如IoC容器、AOP等);
(3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
(4) 支持各种请求资源的映射策略。
MySQL默认连接数,到安装目录里my.ini查看修改,默认100
分页:数据一段显示不完,分段显示
存储过程:创建时编译,以后一直使用,创建一次
数据库优化过程方式
存储引擎:
索引:
弊端:占用磁盘空间,对增删改有影响,变慢
场景
索引结构:

sql语句优化:
数据库锁:
乐观锁, 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
应用场景:解决数据一致性的问题,防止并发过多导致数据出现偏差
数据库实现排他锁
缺点:
1、这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。 2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。 3、这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。 4、这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。
解决方案: 1、数据库是单点?搞两个数据库,数据之前双向同步。一旦挂掉快速切换到备库上。 2、没有失效时间?只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。 3、非阻塞的?搞一个while循环,直到insert成功再返回成功。 4、非重入的?在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。
redis实现
缺点:
在这种场景(主从结构)中存在明显的竞态: 客户端A从master获取到锁, 在master将锁同步到slave之前,master宕掉了。 slave节点被晋级为master节点, 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!
缺点:
性能上可能并没有缓存服务那么高。因为每次在创建锁和释放锁的过程中,都要动态创建、销毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同不到所有的Follower机器上。
其实,使用Zookeeper也有可能带来并发问题,只是并不常见而已。考虑这样的情况,由于网络抖动,客户端可ZK集群的session连接断了,那么zk以为客户端挂了,就会删除临时节点,这时候其他客户端就可以获取到分布式锁了。就可能产生并发问题。这个问题不常见是因为zk有重试机制,一旦zk集群检测不到客户端的心跳,就会重试,Curator客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。(所以,选择一个合适的重试策略也比较重要,要在锁的粒度和并发之间找一个平衡。)
上面几种方式,哪种方式都无法做到完美。就像CAP一样,在复杂性、可靠性、性能等方面无法同时满足,所以,根据不同的应用场景选择最适合自己的才是王道。
从理解的难易程度角度(从低到高)
数据库 > 缓存 > Zookeeper
从实现的复杂性角度(从低到高)
Zookeeper >= 缓存 > 数据库
从性能角度(从高到低)
缓存 > Zookeeper >= 数据库
从可靠性角度(从高到低)
Zookeeper > 缓存 > 数据库
进程包含多个线程
线程实现方式:实现Runnable接口或者继承Thread类或者实现Callable接口(Callable实现需要实现new FutureTask调用)
//线程实现Callable接口的多线程方法,带返回参数泛型
public class TestCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "132456";
}
public static void main(String[] args) {
TestCallable testCallable = new TestCallable();
FutureTask<String> stringFutureTask = new FutureTask<>(testCallable);
new Thread(stringFutureTask).start();
try {
System.out.println(stringFutureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
从定义中可以发现,在 Thread 类中的 run() 方法调用的是 Runnable 接口中的 run() 方法,也就是说此方法是由 Runnable 子类完成的,所以如果要通过继承 Thread 类实现多线程,则必须覆写 run()。
实际上 Thread 类和 Runnable 接口之间在使用上也是有区别的,如果一个类继承 Thread类,则不适合于多个线程共享资源,而实现了 Runnable 接口,就可以方便的实现资源的共享。
线程状态变化:
要想实现多线程,必须在主线程中创建新的线程对象。任何线程一般具有5种状态,即创建,就绪,运行,阻塞,终止。下面分别介绍一下这几种状态:
在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread 类的构造方法来实现,例如 “Thread thread=new Thread()”。
新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。
当就绪状态被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的 run() 方法。run() 方法定义该线程的操作和功能。
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
线程调用 stop() 方法时或 run() 方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。
在此提出一个问题,Java 程序每次运行至少启动几个线程?
回答:至少启动两个线程,每当使用 Java 命令执行一个类时,实际上都会启动一个 JVM,每一个JVM实际上就是在操作系统中启动一个线程,Java 本身具备了垃圾的收集机制。所以在 Java 运行时至少会启动两个线程,一个是 main 线程,另外一个是垃圾收集线程。
三种方式的优缺点
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。 (2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。 (2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
(1)Callable规定的方法是call(),Runnable规定的方法是run(). (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得 (3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常 (4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
start()和run()的区别
同步以及死锁
锁机制:
一个多线程的程序如果是通过 Runnable 接口实现的,则意味着类中的属性被多个线程共享,那么这样就会造成一种问题,如果这多个线程要操作同一个资源时就有可能出现资源同步问题。
同步方法
除了可以将需要的代码设置成同步代码块外,也可以使用 synchronized 关键字将一个方法声明为同步方法。
死锁
同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如,现在张三想要李四的画,李四想要张三的书,张三对李四说“把你的画给我,我就给你书”,李四也对张三说“把你的书给我,我就给你画”两个人互相等对方先行动,就这么干等没有结果,这实际上就是死锁的概念。
所谓死锁,就是两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。
如何避免死锁? 在有些情况下死锁是可以避免的。三种用于避免死锁的技术:
CAS互相交换:乐观锁的一种方式,不加锁synchronized,更新值查看有没有被人改过,没有就更新,有就读取值重新更新
Volatitle开启线程之间的可见性
mesi协议和锁总线
注册中心Eureka
服务网关
Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
SpringCloud Gateway(和zool区别:路由、断言、过滤器)
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
(2)集成 Hystrix 断路器
(3)集成 Spring Cloud DiscoveryClient
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
(5)具备一些网关的高级功能:动态路由、限流、路径重写
服务调用Open Feign
熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
Ribbon(负载均衡)
提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。
配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。
消息总线Bus
事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
第一次当课代表
## 编程基础(掌握)
### JAVA语法
#### Java基础
#### JVM - 类加载机制 - 字节码执行机制 - JVM内存模型 - GC垃圾回收 - JVM性能监控与故障定位 - JVM调优
#### 多线程 - 并发编程的基础 - 线程池 - 锁 - 并发容器 - 原子类 - JUC并发工具类### 数据结构和算法 #### 数据结构 - 字符串 - 数组 - 链表 - 堆、栈、队列 - 二叉树 - 哈希 - 图
#### 算法 - 排序 - 查找 - 贪心 - 分治 - 动态规划 - 回溯### 计算机网络 - ARP协议 - IP、ICMP协议 - TCP、UDP协议 - DNS、HTTP/HTTPS协议 - Session/Cookie### MySQL数据库 - SQL语句的书写 - SQL语句的优化 - 事务、隔离级别 - 索引 - 锁### 操作系统 - 进程、线程 - 并发、锁 - 内存管理和调度 - I/O原理### 设计模式 - 单例 - 工厂 - 代理 - 策略 - 模板方法 - 观察者 - 适配器 - 责任链 - 建造者
——————————————————————————
### 前端(了解) - 基础套餐(大致了解,2-3天) - 三大件 - HTML - JavaScript - CSS - 基础库 - jQuery - Ajax - 模板框架 - JSP/JSTL(已过时) - Thymeleaf - FreeMarker - 组件化框架 - Vue - React - Angular-----------------------------------------------
## 运维知识(配置) - Web服务器 - Nginx - 应用服务器 - Tomcat - Jetty - Undertow - CDN加速 - 持续集成/持续部署 - Jenkins - 代码质量检查 - sonar - 日志收集和分析 - ELK
-----------------------------------
## 成神之路 - 徒手撕源码 - 光脚造轮子 - 闭着眼睛深度调优 - 吊打面试官
-----------------------------------------------
## 研发工具 ### 集成开发环境 - Eclipse - Intellij IDEA - VSCode
### Linux系统(了解) - 常用命令 - Shell脚本### 项目管理/构建工具(掌握) - Maven - Gradle### 代码管理工具(了解) - SVN - Git
——————————————————
## 应用框架 - 数据库框架 - ORM层框架(掌握) - Mybatis - Hibernate - JPA - 连接池(掌握) - Druid - HikariCP - C3P0 - 分库分表 - MyCAT - Sharding-JDBC - Sharding-Sphere - 搜索引擎(了解) - ElasticSearch - Solr - 分布式/微服务(了解,2-3week) - 服务发现/注册 - Eureka - Consul - Zookeeper(重要) - Nacos - 网关 - Zuul - Gateway - 服务调用(负载均衡) - Ribbon - Feign - 熔断/降级 - Hystrix - 配置中心 - Config - Apollo - Nacos - 认证和鉴权(稍微重要些) - Spring Security - OAuth2 - SSO单点登录 - 分布式事务 - JTA接口——Atomikos组件 - 2PC、3PC - XA模式 - TCC模式——tcc-transaction、ByteTCC、EasyTransaction、SeaTa - SAGA模式——ServiceComb、Seata - LCN模式——tx-Icn - 任务调度 - Quartz - Elastic-Job - 链路追踪和监控 - Zipkin - Sleuth - Skywalking - 日志分析与监控——ELK - ElasticSearch - Logstash - Kibana - 虚拟化/容器化 - 容器化——Docker - 容器编排技术——Kubernetes、Swarm
重写(Overriding) | 重载(Overloading) | |
---|---|---|
类的数量 | 父子类、接口与实现类 | 本类 |
方法名称 | 一致 | 一致 |
参数列表 | 一定不能修改 | 必须修改 |
返回类型 | 一定不能修改 | 可以修改 |
异常 | 可以减少或删除,但不能扩展 | 可以修改 |
注册中心Eureka
服务网关
Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
SpringCloud Gateway(和zool区别:路由、断言、过滤器)
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
(2)集成 Hystrix 断路器
(3)集成 Spring Cloud DiscoveryClient
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
(5)具备一些网关的高级功能:动态路由、限流、路径重写
服务调用Open Feign
配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。
消息总线Bus
事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
1.自动装载 自动装载,即将 Bean 装入 IOC。 首先 springboot 通过 maven 的方式,继承于父工程来获得所有 jar。但是 jar 太多,不可能都装入,因此底层通过 classloader+注解的方式,将满足条件的必要的 jar 装入 IOC。 2.自动配置 自动配置,既然要装入 IOC,那么就要完成对象的初始化,即自动配置,而自动配置也是通过注解+class 的方式完成。将配置写在一个标记有特定注解的类中,然后通过注解找到该类,完成对对象的初始化。
简单的说 springboot 的自动装载机制就是通过配置@EnableAutoConfiguration 将配置为@Configuration 下的@Bean 方法加载到 spring 容器中,这个过程就是 spring 自动装载机制。首先 springboot 自动装配功能是为了满足啥?是为了满足其他的插件进行扩展,因为有很多外部的 bean 我们没法管理,也不知道具体包的路径,这个时候 springboot 提供了自动装配功能,让我们外部的类能够注入到 spring 项目中。第二,如果说这个是 springboot 的自动装配功能不如说是 spring 的自动装配功能。因为 springboot 使用了 spring3.1 出来的 ImportSelector 动态 bean 的装载实现的自动装载机制,同时使用了 META-INF/spring.factories 中的 SPI 机制实现了 spring 自动扫描到自动装载的 bean 的机制。后面再说几点,spring 发展是由 XML 文件到注解方式的一个循序渐进的过程,比如@Component 以及它的派生注解@Controller 等,最后 spring 直接把 XML 文件变成了@Configuration 注解,这样理解就可以理解成 springboot 自动装载机制是把外部的 xml 文件的 Bean 配置导入到了自己的项目中,让 Bean 在自己的项目中运行。而起到关键作用的@EnableAutoConfiguration 只是作为了一个中介者的作用。
E1、线程池的优势
(1)、降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗; (2)、提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行; (3)方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。 (4)提供更强大的功能,延时定时线程池。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1、corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
2、maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
3、keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
4、workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
5、threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
5、handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。
线程池流程
1、判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务。已满则。 2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。 3、判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和策略。
(1、判断线程池中当前线程数是否大于核心线程数,如果小于,在创建一个新的线程来执行任务,如果大于则 2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。 3、判断线程池中当前线程数是否大于最大线程数,如果小于,则创建一个新的线程来执行任务,如果大于,则执行饱和策略。)
回到了非线程池缺点中的第3点: 1、因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换。
另外回到了非线程池缺点中的第1点: 2、创建线程池的消耗较高。 或者下面这个网上并不高明的回答: 2、线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。 当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。 使得在线程不至于一直占用cpu资源。
(线程执行完任务后通过循环再次从任务队列中取出任务进行执行,代码片段如下 while (task != null || (task = getTask()) != null) {})。
不用阻塞队列也是可以的,不过实现起来比较麻烦而已,有好用的为啥不用呢?
CPU密集型任务 尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,会造成CPU过度切换。
IO密集型任务 可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间。
混合型任务 可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。 因为如果划分之后两个任务执行时间有数据级的差距,那么拆分没有意义。 因为先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失。
Executors类提供了4种不同的线程池:newCachedThreadPool, newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor
java线程池对比
1、newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成cpu过度切换)
2、newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)
3、newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。
4、newScheduledThreadPool:适用于执行延时或者周期性任务。
1、execute(),执行一个任务,没有返回值。 2、submit(),提交一个线程任务,有返回值。 submit(Callable task)能获取到它的返回值,通过future.get()获取(阻塞直到任务执行完)。一般使用FutureTask+Callable配合使用(IntentService中有体现)。
submit(Runnable task, T result)能通过传入的载体result间接获得线程的返回值。 submit(Runnable task)则是没有返回值的,就算获取它的返回值也是null。
Future.get方法会使取结果的线程进入阻塞状态,知道线程执行完成之后,唤醒取结果的线程,然后返回结果。
https://www.cnblogs.com/dolphin0520/p/3949310.html
Sign in for post a comment
Comments ( 2 )