Java常见异常
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组下标越界异常:ArrayIndexOutOfBoundsException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
类未找到异常:ClassNotFoundException
方法未找到异常:NoSuchMethodException
分布式架构节点
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
HashSet的初始化会进行什么操作
创建一个HashSet会进行new一个HashMap
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不
变,此类允许使用null元素。
在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT =
new Object();
,(定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)
Redis为什么快
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
分布式幂等性问题
幂等操作:是其任意多次执行所产生的影响均与一次执行的影响相同(不用担心重复执行会对系统造成改变)
幂等性解决方案
1.数据库表加唯一索引,防止新增脏数据。
比如对订单号进行加唯一索引,防止生成重复订单。
如果不加索引的后果是:当根据订单号去支付,支付表生成两条重复的订单号,然后去支付宝、微信、易宝支付去支付,付款
成后,第三方异步回调接口,本地接口首先根据订单号查询实体,发现查询到两条,系统就会抛出异常。
2.分布式锁
利用redis,在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路
3.token机制,防止重复提交
a. 数据提交前要向服务的申请token,token放到redis中,token有效时间
b. 提交后后台校验token,同时删除token,生成新的token返回
分布式的弊端
- 架构设计变得复杂(尤其是其中的分布式事务)
- 部署单个服务会比较快,但是如果一次部署需要多个服务,部署会变得复杂
- 系统的吞吐量会变大,但是响应时间会变长
- 运维复杂度会因为服务变多而变得很复杂
- 架构复杂导致学习曲线变大
- 测试和查错的复杂度增大
- 技术可以很多样,这会带来维护和运维的复杂度
- 管理分布式系统中的服务和调度变得困难和复杂
什么时候需要使用索引,什么时候不需要
什么时候需要创建索引
-
主键自动建立唯一索引
-
频繁作为查询条件的字段应该创建索引
-
查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找
-
查询中统计或者分组的字段;
什么时候不需要创建索引
-
频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件
-
where条件里用不到的字段,不创建索引;
-
表记录太少,不需要创建索引;
-
经常增删改的表;
-
数据重复且分布平均的字段,因此为经常查询的和经常排序的字段建立索引。注意某些数据包含大量重复数据,因此他建立索引就没有太大的效果,例如性别字段,只有男女,不适合建立索引。
什么时候需要用存储过程
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL 语句每执行一次就编译一次所以使用存
储过程可提高数据库执行速度。
2.当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,Delete 时)可将此复杂操作用存储过程封装起来与数据库提
供的事务处理结合一起使用。这些操作,如果用程序来完成,就变成了一条条的SQL 语句,可能要多次连接数据库。而换成存
储,只需要连接一次数据库就可以了。
3.存储过程可以重复使用:可减少数据库开发人员的工作量。
4.安全性高:可设定只有某此用户才具有对指定存储过程的使用权。
5.更强的适应性:由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情
况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。
6.分布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。一般来说,存储过程的编写比基本SQL语句
复杂,编写存储过程需要更高的技能,更丰富的经验。
MySql查询语句的执行流程
权限校验(如果命中缓存)-> 查询缓存 -> 分析器 -> 优化器 -> 权限校验 -> 执行器 -> 引擎
MySql更新语句的执行流程
分析器 -> 权限校验 -> 执行器 -> 引擎 -> redo log(prepare状态)-> binlod -> redo log(commit状态)
如何合理设置线程池大小
1、CPU密集型
尽量使用较小的线程池,一般CPU核心数+1
因为CPU密集型任务CPU的使用率很高,若开过多的线程,只能增加线程上下文的切换次数,带来额外的开销
2、IO密集型
方法一:可以使用较大的线程池,一般CPU核心数 * 2
IO密集型CPU使用率不高,可以让CPU等待IO的时候处理别的任务,充分利用cpu时间
方法二:线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
下面举个例子:
比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
3、混合型
可以将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,按情况而定
一次完整的HTTP请求过程
1.对www.baidu.com这个网址进行DNS域名解析,得到对应的IP地址
2.根据这个IP,找到对应的服务器,发起TCP的三次握手
3.建立TCP连接后发起HTTP请求
4.服务器响应HTTP请求,浏览器得到html代码
5.浏览器解析html代码,并请求html代码中的资源(如js、css图片等)(先得到html代码,才能去找这些资源)
6.浏览器对页面进行渲染呈现给用户
7.断开TCP连接
浅拷贝和深拷贝的区别及实现
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化
a 通过拷贝构造方法实现浅拷贝
b 通过重写clone()方法实现浅拷贝
深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
a 通过重写clone方法来实现深拷贝
b 通过对象序列化实现深拷贝
头插法实现链表反转
public ListNode ReverseList(ListNode head) {
ListNode newList = new ListNode(-1);
while (head != null) {
ListNode next = head.next;
head.next = newList.next;
newList.next = head;
head = next;
}
return newList.next;
}
Java创建进程及进程同步
//如何用java语言开启一个进程
public class ProcessDemo {
public static void main(String[] args) throws IOException{
//方式一:使用Runtime的exec方法
Runtime.getRuntime().exec("notepad");
//方式二:使用ProcessBuilder类中的start方法
ProcessBuilder pb = new ProcessBuilder("notepad");
pb.start();
}
}
解决进程同步
1 文件锁
//文件锁的简单实现方法
RandomAccessFile raf=new RandomAccessFile("c:/aa.txt","rw");
FileLock lock=raf.getChannel().lock();
//需要同步的代码
lock.release();
2 使用数据库锁或者第三方获取锁的服务器。如果想进程之间共享数据,可以把数据放到数据库中进行共享。
数据库优化
为什么要优化
- 系统的吞吐量瓶颈往往出现在数据库的访问速度上
- 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
- 数据是存放在磁盘上的,读写速度无法和内存相比
如何优化
- 设计数据库时:数据库表、字段的设计,存储引擎
- 利用好MySQL自身提供的功能,如索引等
- 横向扩展:MySQL集群、负载均衡、读写分离
- SQL语句的优化(收效甚微)
Redis数据类型及数据结构
Redis数据类型 | String | List | Hash | Set | ZSet |
底层数据结构 | 数组 | 双向链表 | 二维结构 第一维度:数组 第二维度:链表 | Hash | Hash+跳跃表 |
为什么要使用消息队列MQ
线程池的七个参数及含义
- corePoolSize: 线程池核心线程数
- maximumPoolSize:线程池最大数
- keepAliveTime: 空闲线程存活时间
- unit: 时间单位
- workQueue: 线程池所使用的缓冲队列
- threadFactory:线程池创建线程使用的工厂
- handler: 线程池对拒绝任务的处理策略
线程池的四种拒绝策略
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,执行后面的任务
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
B-树的特征:
-
根结点至少有两个子女。
-
每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m
-
每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m
-
所有的叶子结点都位于同一层。
-
每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。
B+树的特征:
-
有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
-
所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
-
所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
B+树的优势:
-
单一节点存储更多的元素,使得查询的IO次数更少。
-
所有查询都要查找到叶子节点,查询性能稳定。
-
所有叶子节点形成有序链表,便于范围查询。