面试套路如下:
1.请介绍一个你做过的感觉对自己提高最大的项目。(一般就是接下来围绕这个项目问题,所以最好提前准备点亮点);
2.围绕这个项目面试官会提问,所以回答的时候最好提前把业务流程和项目架构想清楚。比如你们的消息系统怎么做到幂等的,你们分库分表用了这种策略,那么以后怎么继续扩容等。如果在这一环节能把自己做过的项目讲清楚,那就开始下一轮;
3.第三步通常会问你最擅长什么?这就看个人能力了,XX通常喜欢有特色的技术人员。比如你说你擅长数据库调优,那就会围绕数据库调优来问,如果你擅长架构设计,那就举一个你设计的案例,并分析一下为啥这么设计,如果你说你擅长解决线上问题,那么就举一个你自己觉得印象深刻的问题,说一下你咋解决的,这里切记要提前想好,否则面试官会觉得你很平淡,不容易过,这一环节,最好有让人耳目一新的感觉。
4.如果以上都过了,接下来就是HR面试,不要表现太随意,不要乱说话。所以切记,不要和HR谈薪水,不要揪着某件事不放,另外,所有的HR都会问,你如何把你擅长的技术分享给你的同事,这个问题要提前准备,回答太简单了,HR会觉得你的价值只是写代码而已。
宏观问题
- 如何设计一个高并发的系统(数据异构,冷热分离,使用缓存)
- 如何度过服务器启动后的连接风暴 (感觉不是很靠谱,不过却提供了一点思路)
- 单机上一个线程池正在处理服务如果忽然断电该怎么办?(正在处理和阻塞队列里的请求怎么处理)?
- 并发情况下的重复提交问题。(幂等性问题)
- 超大数据量(主要被检索)在内存里的存储用什么数据结构合适
- 锁的优化策略
- 简单的秒杀实现 (别人的git项目)
- 双11应对策略(这个跟秒杀是完全不同的概念)
- 分布式和集群的区别
- String实现(考察String源码的研究)
- 微服务和SOA的区别
- 常用设计模式
- 1000W条数据中随机取10条
- Java代码性能优化tips
- 领域驱动设计
- 什么是OSGI?
- 自适应负载均衡
知识点
- jdk 1.8的新特性
- Http,Https协议
- Vector和ArrayList
- hash冲突的解决方法
- Java集合类
- String/StringBuffer/StringBuilder
- Collection/Collections
- == 和 equals的区别 equals和hashcode
- equals()改写的注意事项
- Set/List/Map/Vector
- Linux常用命令,查看端口,内存使用情况,cpu使用情况等
- telnet和ping的区别
- 正确理解HTTP短连接中的Cookie、Session和Token
- HashMap/HashTable
- maven如何解决依赖冲突问题
- 如果要将一个对象作为HashMap的key,那么这个对象该实现什么方法。
- ArrayList/LinkedList比较,以及线程安全问题
- Java对象的深复制(深克隆)和浅复制(浅克隆)
- jdk/jre/jvm 三者的关系
- 集群session如何共享
- 接口和抽象类有何不同,为什么要区分
- tomcat 生命周期
- tomcat 接收用户数据的IO原理
- TCP/UDP区别
- 重定向/转发 的区别
- 三次握手/四次挥手
- 1.cpu不高但是load值高,是怎么回事? 2.如果cpu高,你怎么找到问题所在
- JAVA是值传递还是引用传递?
- JMM - java内存模型
- LinkedBlockingQueue和ArrayBlockingQueue的区别
- static和final
- 方法的重载和重写
- public/protected/default/private四种访问修饰符的控制范围
- throwable/exception/error/runtimeException
数据结构
- HashMap
- 红黑树
- WeakHashMap
- CopyOnWrite系列
- BST(Binary Search Tree)
- Trie
- SkipList
- List(ArrayList/LinkedList…)
- Vector
- Stack
- Queue
- B树和B+树
算法
由于csdn不支持checkbox,这里就用粗糙的√ 来表示完成
编程题
- 全排列(Full Permutation)
- 快排
- 归并排序
- 反转单链表(递归/非递归)
- 判断链表是否有环,进阶求环入口
- 桶排序
- 计数排序
- 基数排序
- 字符串的反转
- Top-k
工程实践
- n个不定长数组,如何尽快合并并排序
- 判断一个树是不是完全二叉树
- 两个栈模拟队列
- 判断两个二叉树相等
- 二分查找
- 斐波那契数列
- LRU
- 开机排名实现
- hashmap不用同步关键字实现get,put线程安全
- 若有1T的数据,需要实现由大到小排序,你用什么办法,说说你的思路和想法
- 读取文件(url.txt,里面每一行是一个网址,有重复的),然后记录每个网址出现的次数,按照出现次数由高到低进行排序,并且输出到txt文件中,一行是网址,下一行是该网址的出现次数,按照这个格式来输出。(蚂蚁电面)
- 实现String的indexOf方法
- 外部排序,多路归并
- 蓄水池(单机与分布式)
- 布隆过滤器(Bloom Filter)
- 布谷鸟过滤器
- 堆排序
- 希尔排序
- 大数取余
- 银行家算法
- murmurHash
数据库
主要针对Mysql方面
索引的好文
- sql语句是怎么被数据库执行的
- explain的用法
- 幻读
- mysql的锁机制、乐观锁悲观锁
- MVCC (多并发版本控制)
- mysql数据库同步
- mysql引擎及其应用场景
- mysql查询优化
- 行级锁/表级锁/共享锁/排他锁/gap锁/next-key锁
- 数据库死锁以及死锁检测
- BTREE索引/ HASH索引/ FULLTEXT索引
- 位图索引(oracle限定)
- mysql为什么使用B+树而不是B树来实现索引
- 聚集索引
- 使用索引的原则
- 数据库的水平拆分和垂直拆分
- select … for update的用法
- left join/right join/inner join
- group by
- having
- in
- 行转列
- 数据库调优的基本方法和步骤(逻辑移入代码,优化sql,读写分离,分表,分区,分布式等)
- InnoDB和MyISAM的区别
- 多对多表结构设计
- sql的优化
- sql注入
- mysql数据库事务隔离级别
- drop/delete/truncate 的区别
- MySQL中如何定位查询效率较慢的SQL语句(重点是如何定位)
- 什么情况会导致索引失效
- 唯一索引和主键的区别
- 联合索引使用时的注意点(最左原则等)
- sql语句条件的顺序是否影响sql的执行效率(如何影响?涉及索引/查询优化器等)
- 数据库三范式
- 反范式
- 数据库连接池
- 共享锁(LOCK IN SHARE MODE)和排它锁(FOR UPDATE) (共享锁可以多事务同步读,限制读以外的行为,排它锁限制所有行为)
- redo log和undo log
- 游标(cursor)
- 从表中随机取N条数据
- 用户变量@xxx和系统变量@@xxx
- mysql中 = 和 :=的区别(=代表是否相等,:=是赋值操作)
- 分组排序
- 用in做查询的时候可能会出现的全表扫描情况,以及优化措施(高版本mysql5.6之后直接用join优化了,低版本需要自己改写语句为join的形式)
- Foreign Key的使用 √
- Druid数据库连接池
- 数据库中间件 MyCAT
- LVS+Keepalived实现MySQL负载均衡
多线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AZac1sQ8-1571844072120)(http://note.youdao.com/yws/res/32493/WEBRESOURCEd339658ce751695bd08ba8ae4bf6f299)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ID6wDQcU-1571844072121)(http://note.youdao.com/yws/res/31352/WEBRESOURCE96356d007a68a1eca98ae45d2f2fb5ee)]
4. synchronized的用法
5. 死锁是什么?如何排查死锁?遇到死锁接下来怎么办?
6. wait和notify
7. CAS
8. AQS
9. MESI
10. volatile作用
11. 线程池的各个要素以及种类和运行机理
12. ConcurrentHashMap 1.7
13. ConcurrentHashMap 1.8
14. volatile和static在多线程下的区别 √
15. ConcurrentModificationException异常出现的原因/fast-
16. yield和sleep的区别
17. wait和sleep的异同
18. sleep(0)和sleep(n)的区别
19. notify和notifyall的区别 √
20. TreadLocal是什么?能做什么?
21. Netty之FastThreadLocal
22. CopyOnWriteArrayList √
23. 可重入锁的概念
24. ReentrantLock(重入锁) 和 synchronized
25. ReadLock/WriteLock[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qk80Hjxw-1571844072122)(https://note.youdao.com/yws/res/11565/E2D81819D4E14CC2B1BCCA5968A77547)]
26. 线程协调工具(jstack等吗?)
27. LockSupport
28. 生产者消费者 √
29. 三个线程交替执行 A-B-C-A-B-C… √
30. 线程池的corePoolSize和maximumPoolSize的区别
31. CountDownLatch 的概念及使用
32. CyclicBarrier (栅栏)的概念及使用
33. Semaphore (信号量)的概念及使用
34. synchronized的可重入性
35. Future 模式
36. Master-Slave 模式
37. 有哪些锁(公平/非公平,互斥锁偏向锁等)
38. 多个线程池和单个线程池的区别
39. CPU密集型/IO密集型/混合型
40. 如何合理地估算线程池大小?
41. 守护线程Daemon
42. 轻量级锁
43. CompletableFuture
44. fail-fast和fail-safe
45. Future模式
46.
IO
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ar0WL6Oq-1571844072123)(http://note.youdao.com/yws/res/31927/AA7C7D13C3FE40D6991138F95105AA38)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2opI2tlA-1571844072124)(http://note.youdao.com/yws/res/31925/176D7792704042A5A2F76AFFB0C38D38)]
- nio简介:nio和io有什么区别
- Buffer
- Channel
- Selector
- select, poll, epoll
- C10K问题
JVM
别人的专栏
GC
G1收集器:并发运行;可作用于新生代或老年代;标记-整理算法+复制算法;响应速度优先;面向服务端应用。
收集器 | 串行or并行 | 作用范围 | 使用算法 | 特性 | 适用范围 |
---|---|---|---|---|---|
Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 适用于单CPU环境下的client模式 |
Serial Old | 串行 | 老年代 | 标记-整理 | 响应速度优先 | 单CPU环境下的Client模式 |
ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境Server模式下与CMS配合使用 |
Parallel Scavenge | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
Parallel Old | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
CMS | 并行 | 老年代 | 标记-清除 | 响应速度优先 | 适用于互联网或B/S业务 |
G1 | 并行 | 新生代或老年代 | 标记-整理算法+复制算法 | 兼顾吞吐量和响应速度 | 面向服务端应用 |
- JVM GC算法
- Minor GC、Old GC和Full GC的区别和触发条件
- Card Marking
- 标记清除
- 标记整理
- 复制清除
- 方法区的回收
垃圾回收器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNMEHWn7-1571844072124)(http://note.youdao.com/yws/res/31383/WEBRESOURCEc6bf0f7335d3ae49f92243fb7875db75)]
- 查看JVM使用什么垃圾回收器
- Serial GC
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8q3UAbX-1571844072125)(http://note.youdao.com/yws/res/31551/WEBRESOURCE9deefe87c366a2e8d4ae089fa45738b9)]
3. Parallel GC
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NS81o7jp-1571844072126)(http://note.youdao.com/yws/res/31554/WEBRESOURCEc76387da92ea84206b9a296abcd04969)]
- CMS(Concurrent Mark Sweep)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q9xXEVdw-1571844072127)(http://note.youdao.com/yws/res/31557/WEBRESOURCE8202cd47b294fe72c0662de7f875c35f)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SF6kcq3l-1571844072130)(http://note.youdao.com/yws/res/31566/WEBRESOURCEb91b38de82c0e01affe6da2662055c2a)]
- ZGC
JVM调优
- JVM调优常用指令
- JVM内存优化 xmx,xms设置
- Full GC如何排查
- 暂停时间与吞吐量的调和
- Plumbr
其他概念(类加载编译内存模型等)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeO4hnLw-1571844072130)(http://note.youdao.com/yws/res/31725/WEBRESOURCEad164451f0a0f2bf4002411f44179369)]
- JAVA内存模型 √
- JMM √
- 强引用/软引用/弱引用/幻象引用
- Java类加载过程
- Java类加载的方式
- 双亲委派
- 自己定义和实现类加载器
- 破坏双亲委派
- java.lang.instrument的使用
- 能否加载自己定义的java.lang.String
- jar hell(这玩意就是jar包冲突)
- 写代码实现堆溢出、栈溢出、永久代溢出、直接内存溢出
- 类隔离机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ma5NvZoy-1571844072131)(http://note.youdao.com/yws/res/32078/F9E3E8F5681046419D126E2C67B8E3FF)]
设计思想
设计模式
- 代理模式(静态代理/动态代理)
- 工厂模式(简单工厂/普通工厂/抽象工厂) 为什么使用工厂模式
- 单例模式(分类/写法/多线程下/应用场景)
- 观察者模式 (重点在于被观察对象持有观察者的引用,理解一下我说的)
- 策略模式
- 责任链模式
- 模板模式
- 享元模式(Flyweight), 缓存基于的设计模式
- 责任链模式
- 静态代理模式
- 动态代理模式
- 模板模式
- 装饰器模式
- 适配器模式
- 桥接模式
其他
- jdk中实现的设计模式
- 关联、依赖、聚合、组合
- API和SPI
- OSGI(热插拔&模块化)
- 单点登录(SSO)
Java web
Spring
- Spring的优点
- Springboot的启动过程
- Spring AOP
- Spring IOC
- 如何自己搭建一个Spring框架
- @Component 和 @Autowired
- Spring bean的生命周期
- 多线程获取Spring自动注入的对象为null
- spring的两种动态代理: jdk和cglib的区别和实现
- Spring 如何解决bean循环依赖
- Spring bean有哪些作用域
- Spring 中bean的线程安全性
- Spring 自动装配模式
- Spring 控制器的加载过程
- Spring 用了什么设计模式
- Spring 事物的种类和各自的区别
- Spring 事务传播行为
- Spring 事务的实现方式和实现原理
- Spring AOP里面的几个名词
- Spring 通知有哪些类型
- Spring 两大核心接口
- DispatcherServlet的作用
- 你用Spring实现过什么高级功能
- SpringBoot自动装配原理分析
- fixedRate和fixedDelay的区别
https://blog.csdn.net/ying847782627/article/details/51508303
https://www.cnblogs.com/javapath/p/6862063.html
MyBatis
- sqlSession是干啥子的
- mybatis的缓存机制
- $ 和 #的使用
- dao接口跟xml文件里面的sql是如何建立关系的
其他
- servlet
分布式
- CAP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vZrzWhBW-1571844072132)(http://note.youdao.com/yws/res/31985/BAA7DA703F9E4566A36E4E2496CF4C5F)]
- 分布式锁
- 分布式事务(2PC, 3PC, Paxos…)
- 分布式缓存(Redis Cluster…)
- 分布式搜索(ElasticSearch…)
- 分布式计算
- 分布式存储
- 分布式系统中全局ID的生成 https://juejin.im/post/5b3a23746fb9a024e15cad79 | https://blog.csdn.net/wangzhen199009/article/details/38761575 | https://juejin.im/entry/59eb02806fb9a0451049a0ab
- 容灾
- 降级
- 熔断
- CAP的P
- 幂等性问题
Dubbo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MK2EyCVX-1571844072133)(https://note.youdao.com/yws/res/11563/C38AD03573304123A845BD03DDE1FD3B)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7dN37tg-1571844072134)(http://note.youdao.com/yws/res/31988/12CB8C5FE57D43269EB3E03692025BD3)]
- Dubbo常用协议有哪些
- Dubbo是如何通信的
- Dubbo的模块有哪些
- Dubbo的扩展点加载
- Dubbo序列化的方式是什么, 默认序列化方式(Hessian2)
- 如果自己实现Dubbo, 有什么思路么
- Dubbo的负载均衡策略有哪些
- Dubbo配置中心是什么鬼
- 基于zookeeper实现的分布式锁
- Dubbo中如何快速定位哪台provider出了问题
- Dubbo获取ip的坑
- 如何实现简单的RPC
- Dubbo的注册中心使用zookeeper或redis有什么区别
Zookeeper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5V2ePgIO-1571844072134)(http://note.youdao.com/yws/res/32060/0B6453DD85B84047BEC8E25E7B526F3B)]
zookeeper在Dubbo中扮演了一个什么角色,起到了什么作用啊? - 预流的回答 - 知乎
https://www.zhihu.com/question/25070185/answer/86166486
中间件
Redis (Remote Dictionary Server)
- 为什么使用Redis
- Redis和memcache区别
- Redis常见问题
- expire策略
- Redis淘汰策略
- 缓存穿透/缓存击穿/缓存雪崩
- Redis的持久化
- 为什么要使用MQ消息中间件?它解决了什么问题?
- string/list/set/zset/hash 的用法
- 缓存和DB的数据同步问题
- set方法实现分布式锁
- Redis集群
- 先删缓存还是先更新数据库,为什么?
Nginx
ElasticSearch
ElasticSearch的应用场景
维基百科
The Guardian(国外新闻网站)
Stack Overflow(国外的程序异常讨论论坛)
GitHub(开源代码管理)
电商网站
日志数据分析
商品价格监控网站
BI系统
站内搜索
ElasticSearch的特点
可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司
Elasticsearch不是什么新技术,主要是将全文检索、数据分析以及分布式技术,合并在了一起
对用户而言,是开箱即用的,非常简单,作为中小型的应用,直接3分钟部署一下ES
Elasticsearch作为传统数据库的一个补充,比如全文检索,同义词处理,相关度排名,复杂数据分析,海量数据的近实时处理;
和MySQL相应概念的对应
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8HLvHrJ-1571844072135)(http://note.youdao.com/yws/res/31791/D772E9F0CF0741EBA3A7A268CC5428CF)]
将磁盘里的东西尽量搬进内存,减少磁盘随机读取次数(同时也利用磁盘顺序读特性),结合各种奇技淫巧的压缩算法,用及其苛刻的态度使用内存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KsSaXZT6-1571844072136)(https://note.youdao.com/yws/res/11561/C049500DD58C4F9EAA515D10390BCAC2)]
某篇文章的提炼
- 高速的查询来自filesystem cache这一部分,所以这部分相对于es全量数据的占比要尽量大。
- es的内存极其宝贵,不要用她来存储全量数据。存储必要的索引
- es通过索引查询到id,再根据id配合HBase或MySQL来实现全量数据的存储查询
- 【缓存预热子系统】的必要, 预热的缓存当然还是es的filesystem cache
- 不要用es做深度分页,取而代之的是使用scroll(下拉)
- es不适合做关联查询,仅适合作为主搜索引擎。这里回到第三点
Kafka
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3dKryB1-1571844072137)(http://note.youdao.com/yws/res/31191/WEBRESOURCE9d1eca6b8d8dcb1b87d6d1e709c2891b)]
Hystrix
Shell
- shell 统计日志里面ip的数量 (awk脚本)
安全
- XSS
跳转区
Java类加载方式
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法去内,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构。类的加载最终是在堆区内的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
类加载有三种方式:
- 命令行启动应用时候由JVM初始化加载
- 通过
Class.forName()
方法动态加载 - 通过
ClassLoader.loadClass()
方法动态加载
forName方法可以传递你自定义的ClassLoader。
死锁是什么?如何排查死锁?遇到死锁接下来怎么办?
死锁是什么
多个线程相互持有各自需要的锁不释放导致各线程无法继续往下走的情况,我理解为是死锁。
常见的死锁场景:
- 三个人 三根筷子:每个人需要拿到身边的两根筷子才能开始吃饭
- 银行转账问题:线程 A 从 X 账户向 Y 账户转账,线程 B 从账户 Y 向账户 X 转账,那么就会发生死锁。
如何排查死锁
遇到死锁接下来该怎么办?
先删缓存还是先更新数据库,为什么?
应该是先更新数据库,再删缓存。
如果一个线程 A 先把缓存删除了,然后去更新数据库,那么在它删了缓存还没有更新到数据库的这个中间时间,线程B进来了,发现缓存没有,就去读库,这时候还是读取还是旧的数据,然后又更新到缓存去了。此时A 才把新数据写到数据库。此时就产生了一个典型的问题就是“双写不一致”。
关于这块问题的讨论:《缓存更新的套路》-陈皓老师
如何理解CAP中的P
一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。
总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。
正向代理代理客户端,反向代理代理服务器。---- from 知乎某大神一针见血的回答
代理其实就是一个中介,A和B本来可以直连,中间插入一个C,C就是中介。刚开始的时候,代理多数是帮助内网client访问外网server用的(比如HTTP代理),从内到外。后来出现了反向代理,"反向"这个词在这儿的意思其实是指方向相反,即代理将来自外网client的请求forward到内网server,从外到内。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJCr7vLM-1571844072137)(http://note.youdao.com/yws/res/31891/WEBRESOURCE07f01bc048bd9e151e1a4d23c2774184)]
反向代理的实现
- 需要有一个负载均衡设备来分发用户请求,将用户请求分发到空闲的服务器上
- 服务器返回自己的服务到负载均衡设备
- 负载均衡将服务器的服务返回用户
以上的潜台词是:用户和负载均衡设备直接通信,也意味着用户做服务器域名解析时,解析得到的IP其实是负载均衡的IP,而不是服务器的IP,这样有一个好处是,当新加入/移走服务器时,仅仅需要修改负载均衡的服务器列表,而不会影响现有的服务。
谈完反向代理服务,再来谈谈终端用户常用的代理服务。
代理
- 用户希望代理服务器帮助自己,和要访问服务器通信,为了实现此目标,需要以下工作:
- a) 用户IP报文的目的IP = 代理服务器IP
- b) 用户报文端口号 = 代理服务器监听端口号
- c) HTTP 消息里的URL要提供服务器的链接
- 代理服务器可以根据c)里的链接与服务器直接通信
- 服务器返回网页
- 代理服务器打包3)中的网页,返回用户。
代理服务器应用场景
场景一
如果不采用代理,用户的IP、端口号直接暴露在Internet(尽管地址转换NAT),外部主机依然可以根据IP、端口号来开采主机安全漏洞,所以在企业网,一般都是采用代理服务器访问互联网。
那有同学会有疑问,那代理服务器就没有安全漏洞吗?
相比千千万万的用户主机,代理服务器数量有限,修补安全漏洞更方便快捷。
场景二
在一个家庭局域网,家长觉得外部的世界是洪水猛兽,为了不让小盆友们学坏,决定不让小盆友们访问一些网站,可小盆友们有强烈的逆反心理,侬越是不让我看,我越是想看,于是小盆友们使用了代理服务器,这些代理服务器将禁止访问的网页打包好,然后再转交给小盆友,仅此而已。
当然关键的关键是代理服务器不在禁止名单当中!
Redis 过期的key是怎么处理的
面试官:讲讲redis的过期策略如何实现? - Java架构征服的文章 - 知乎
https://zhuanlan.zhihu.com/p/65866107
- 定时随机删除
- 惰性删除
- redis是定时+惰性+内存淘汰机制(比如LRU)
select, poll, epoll
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBv9fnJ3-1571844072138)(http://note.youdao.com/yws/res/31785/D4BD3A3F4D284D878DC90B6344EE144F)]
为什么使用Redis
Redis是个轻量级的高性能的缓存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbj2T0Rv-1571844072139)(http://note.youdao.com/yws/res/31744/72DDEDD89A264306A24CE3F0CF52D92A)]
他的优势
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EbUC5aVo-1571844072140)(http://note.youdao.com/yws/res/31747/3B2D2841AA4047B697240657C50021AF)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sBp3zH5H-1571844072140)(http://note.youdao.com/yws/res/31749/8EFE5E8B2DBB4D7397F151790960DC03)]
MySQL优化流程
- 开启查询缓存,优化查询
- 使用慢查询日志来分析
- explain你的select查询,这可以帮助分析查询语句或是表结构的性能瓶颈。explain的查询结果还会告诉你该表的索引主键被如何利用,数据表是如何被搜索和排序的
- 当只有一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查下一条符合记录的数据。
- 为搜索字段建索引
- 使用enum而不是varchar。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,而且确定这些字段的取值是有限而且固定的,那么应该使用enum而不是varchar
- Prepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以使用它获得很多好处,无论是性能问题还是安全问题。 Prepared Statements可以检查一些绑定好的变量,这样可以保护程序不会受到“SQL注入式”攻击
- 垂直分表
- 选择正确的存储引擎
GC暂停时间与吞吐量
响应能力(Responsiveness)
响应能力就是程序或系统对一个请求的响应有多迅速. 比如:
- 程序UI响应速度有多灵敏
- 网站页面响应有多快
- 数据库查询有多快
对响应速度要求很高的系统, 较大的停顿时间(large pause times) 是不可接受的. 重点是在非常短的时间周期内快速响应.
吞吐量(Throughput)
吞吐量关注在一个特定时间段内应用系统的最大工作量。衡量吞吐量的指标/示例包括:
- 给定时间内完成的事务数.
- 每小时批处理系统能完成的作业(jobs)数量.
- 每小时能完成多少次数据库查询
在吞吐量方面优化的系统, 停顿时间长(High pause times)也是可以接受的。由于高吞吐量应用运行时间长,所以此时更关心的是如何尽可能快地完成整个任务,而不考虑快速响应。
这俩目标是相互矛盾的目标。(所以一部分垃圾回收器是面向吞吐量的(throughput-oriented),另一部分是面向最短暂停时间的)
频繁的GC会带来很多开销,所以我们尽可能的少做GC。
少次多量就是出于吞吐量优先的考虑。
而过长时间的GC又会带来过长的暂停时间,对用户造成一些不好的体验,这时候又需要考虑减小GC的暂停时间多GC几次。。
- CMS侧重的是暂停时间短(因为并行执行)
- ParallelOldGC以及SerialGC面向吞吐量
Parallel GC值得注意的参数:
-XX:MaxGCPauseMillis
-XX:GCTimeRatio
- GCTimeRatio
默认值0-99
如果是99,意味着 99/(99+1)的时间是应用程序工作的时间,剩下的1/(99+1)是gc线程占用的时间。这个比例比较绕,需要关注
- MaxGCPauseMillis
会告诉JVM最大暂停时间的目标值(以毫秒为单位).
默认情况下,最大暂停时间没有被设置。
如果最大暂停时间和最小吞吐量同时设置了目标值,实现最大暂停时间目标具有更高的优先级。 当然,无法保证JVM将一定能达到任一目标,即使它会努力去做。 最后,一切都取决于手头应用程序的行为。
当设置最大暂停时间目标时,我们应注意不要选择太小的值。 正如我们现在所知道的,为了保持低暂停时间,JVM需要增加GC次数,那样可能会严重影响可达到的吞吐量。 这就是为什么对于要求低暂停时间作为主要目标的应用程序(大多数是Web应用程序),会建议不要使用吞吐量收集器,而是选择CMS收集器。
在设计(或使用)GC算法时,我们必须确定我们的目标:一个GC算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷。
布隆过滤器
Sleep(0)与Sleep(n)的区别
Sleep 接口均带有表示睡眠时间长度的参数 timeout。调用以上提到的 Sleep 接口,会有条件地将调用线程从当前处理器上移除,并且有可能将它从线程调度器的可运行队列中移除。这个条件取决于调用 Sleep 时timeout 参数。
当 timeout = 0, 即 Sleep(0),如果线程调度器的可运行队列中有大于或等于当前线程优先级的就绪线程存在,操作系统会将当前线程从处理器上移除,调度其他优先级高的就绪线程运行;如果可运行队列中的没有就绪线程或所有就绪线程的优先级均低于当前线程优先级,那么当前线程会继续执行,就像没有调用 Sleep(0)一样。
当 timeout > 0 时,如:Sleep(1),会引发线程上下文切换:调用线程会从线程调度器的可运行队列中被移除一段时间,这个时间段约等于 timeout 所指定的时间长度。为什么说约等于呢?是因为睡眠时间单位为毫秒,这与系统的时间精度有关。通常情况下,系统的时间精度为 10 ms,那么指定任意少于 10 ms但大于 0 ms 的睡眠时间,均会向上求值为 10 ms。
结论:
由上面的分析可以看出,如果我们想让当前线程真正睡眠一下子,最好是调用 Sleep(n)
synchronized和ReentrantLock
功能 | ReentrantLock | synchronized |
---|---|---|
可重入 | 支持 | 支持 |
非公平 | 支持(默认) | 支持 |
公平锁 | 支持,new ReentrantLock(true) | 不支持 |
中断 | 支持,lockInterruptibly() | 不支持 |
尝试加锁 | 支持,tryLock() | 不支持 |
超时锁 | 支持,tryLock(timeout, unit) | 不支持 |
获取当前线程获取锁的次数 | 支持,getHoldCount() | 不支持 |
检测是否被当前线程占有 | 支持,isHeldByCurrentThread() | 不支持 |
检测是否被任意线程占有 | 支持,isLocked() | 不支持 |
条件锁 | 可支持多个条件,condition.await(),condition.signal(),condition.signalAll() | 只支持一个,obj.wait(),obj.notify(),obj.notifyAll() |
Synchronized与ReentrantLock区别总结(简单粗暴,一目了然)
java同步系列之ReentrantLock VS synchronized——结果有点意外 - 彤哥读源码的文章 - 知乎
https://zhuanlan.zhihu.com/p/68639127
聚集索引
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYYDtbnb-1571844072141)(http://note.youdao.com/yws/res/31180/WEBRESOURCE78317ee9552e384a0458390d1794723e)]
- 聚集索引相对于普通索引来说,存储的是数据库的全量数据(叶子节点存储的是每一行的数据, 如上图)。
- 普通索引在查询索引字段以外的数据的时候,需要根据id跳到聚集索引中去查询。就是所谓的二次查询。
结合下面两篇文章来加深对聚集索引的印象吧
JVM调优常用指令
有一个调优的整体的原则:
- 先做一个JVM的性能测试,了解当前的状态
- 明确调优的目标,比如减少FULL GC的次数,减少GC的总时间,提高吞吐量等
- 调整参数后再进行多次的测试,分析,最终达到一个较为理想的状态。各种参数要根据系统的自身情况来确定,没有统一的解决方案
将各种工具的文章页很多,这里从解决问题的角度出发列出几个。
查看JVM启动参数
-
jps -v
-
jinfo -flags pid
-
jinfo pid -- 列出JVM启动参数和system.properties
-
ps -ef | grep java
查看当前堆的配置
-
jstat -gc pid 1000 3
列出堆的各个区域的大小 -
jstat -gcutil pid 1000 3
– 列出堆的各个区域使用的比例 -
jmap -heap pid
– 列出当前使用的GC算法,堆的各个区域大小
查看线程的堆栈信息
jstack -l pid
dump堆内的对象
-
jmap -dump:live,format=b,file=xxx pid
-
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=xxx
– 设置JVM参数,当JVM OOM时输出堆的dump -
ulimit -c unlimited
– 设置Linux ulimit参数,可以产生coredump且不受大小限制。之前在线上遇到过一个极其诡异的问题,JVM整个进程突然挂了,这时候依靠JVM本身生成dump文件已经不行了,只有依赖Linux,让系统来生成进程挂掉的core dump文件
使用jstack 可以来获得这个coredump的线程堆栈信息:jstack "$JAVA_HOME/bin/java" core.xxx > core.log
获得当前系统占用CPU最高的10个进程,线程
ps Hh -eo pid,tid,pcpu,pmem | sort -nk3 |tail > temp.txt
图形化界面
- jvisualvm 里面有很多插件,比如Visual GC,可以可视化地看到各个堆区域时候的状态,从而可以对整体GC的性能有整体的认识
java.lang.instrument的了解和使用
JMM浅谈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZXqjpus-1571844072142)(https://note.youdao.com/yws/res/11568/F6EAD036AB8E4AA592AEA6E0F697E3CB)]
原子性,可见性,有序性,以及volatile, final跟变量线程安全的一些关系
equals改写的注意事项
为什么equals()方法要重写
判断两个对象在逻辑上是否相等,如根据类的成员变量来判断两个类的实例是否相等,而继承Object中的equals方法只能判断两个引用变量是否是同一个对象。这样我们往往需要重写equals()方法。
我们向一个没有重复对象的集合中添加元素时,集合中存放的往往是对象,我们需要先判断集合中是否存在已知对象,这样就必须重写equals方法。
重写equals方法的要求
- 自反性:对于任何非空引用x,x.equals(x)应该返回true。
- 对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
- 传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
- 一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
- 非空性:对于任意非空引用x,x.equals(null)应该返回false。
hashMap中的equals重写
** **
双亲委派机制的工作过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NAeEVUD-1571844072143)(https://note.youdao.com/yws/res/11567/B24241C2F87847FAA32570F0E20A4D1A)]
- 类加载器收到类加载的请求
- 把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器
- 启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛出异常,通知子加载器进行加载。
- 重复步骤三
以上就是双亲委派机制的原理。
接下来举个例子:
大家所熟知的Object类,直接告诉大家,Object默认情况下是启动类加载器进行加载的。假设我也自定义一个Object,并且制定加载器为自定义加载器。现在你会发现自定义的Object可以正常编译,但是永远无法被加载运行。
这是因为申请自定义Object加载时,总是启动类加载器,而不是自定义加载器,也不会是其他的加载器。
JAVA集合类
给个图吧。。。
Http/Https
-
HTTPS:HTTP协议的安全加强版,通过在HTTP上建立加密层,对传输数据进行加密。主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
-
HTTP无需证书,HTTPS需要CA机构颁发的SSL证书;HTTP工作于应用层,HTTPS工作于传输层(这句话我持保留态度)。
-
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
https更费电,加载时间更长
for update的用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIuswWSE-1571844072145)(https://note.youdao.com/yws/res/11566/94CA77E129604E0CBD287C07FADC4BF5)]
通过connection.commit()操作来释放锁。
也可以单行使用
- 站在资源的角度来看,如果select … for update的资源被锁定(比如行锁或表锁)的话,这个方法会阻塞(非短暂,取决于查询资源的释放时机,会存在超时中断)
- 如果是站在select … for update这条语句的角度来看,执行完毕当然会立刻提交了
telnet和ping的区别
ping通常是用来检查网络是否通畅或者网络连接速度的命令。而telnet是用来探测指定ip是否开放指定端口的。
斐波那契数列
//就写个最简单的形式吧...
int getF(int n){
if(n==1||n==2)
return 1;
else
return getF(n-1)+getF(n-2);
}
然后遇到过让我遍历打印斐波那契的,当时的思路是用for循环调用上面这个递归。
明显这个思路是非常差的,因为每一次递归其实都走了一遍所有的元素,效率非常低
于是有了下面的代码
斐波那契最好还是用循环写,当N越大的时候递归越不好。。递归就是思路好。。
void printFeb(int n){
int a1 = 1;
int a2 = 1;
for(int i=0;i<=n;i++){
if(i==0||i==1){
System.out.println(1);
}else{
int temp = a1+a2;
System.out.println(temp);
a1 = a2;
a2 = temp;
}
}
}
二分查找
int binaryS(int []arr, int t){
int lo=0;
int hi=arr.length-1;
int mid = 0;
while(hi>=lo){
mid = (hi+lo)>>>1; //防止溢出
if(arr[mid]==t){
return mid+1; //返回的不是下标,而是这个元素所在的位置
}else if(arr[mid]<t){
lo = mid+1;
}else if(arr[mid]>t){
hi = mid-1;
}
}
return -1; //一定要有
}
int binarySearchRecur(int[]arr,int key,int lo,int hi){
int mid = (hi+lo)>>>1;
if(hi>=lo){
if(key==arr[mid]){
return mid+1;
}else if(key>arr[mid]){
return binarySearchRecur(arr,key,mid+1,hi);
}else {
return binarySearchRecur(arr,key,lo,mid-1);
}
}
return -1; //一定要有
}
jdk/jre/jvm 三者的关系
TreadLocal是什么?能做什么?
ThreadLocal 可以理解为***线程的局部变量***。
是一种多线程间并发访问变量的解决方案。与synchronized等加锁方式不同,ThreadLocal完全不提供锁,而是以空间换时间的方式为每个线程提供独立的变量副本,以保障线程安全。
就是这个东西,不同的线程自己拥有自己的,线程间不可见。就是说就算访问同一个对象的变量,线程1只能访问到线程1上的修改,线程2访问不到,相互独立。
有点像volatile的对立面(并非严格意义山的,volatile是强制线程去主内存获取更新的变量)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msYwtvOq-1571844072146)(http://note.youdao.com/yws/res/31258/WEBRESOURCEa5e6d11fb4d0e8345bb596c9e5413221)]
当数据量比较小、装载因子小的时候,适合采用开放寻址法。这也是Thread里的threadlocalmap使用开放寻址法来处理hash冲突的原因。
ThreadLocal用途
- 保存线程上下文信息,在任意需要的地方可以获取!
- 线程安全的,避免某些情况线程同步带来的性能损失
- 比如当前线程 id,每个线程都有,各自独立。
- session
- SimpleDateFormat …
- spring事务管理用threadlocal存储Connection
强引用/软引用/弱引用/幻象引用
引用类型 | 取得目标对象方式 | 垃圾回收条件 | 是否可能内存泄露 |
---|---|---|---|
强引用 | 直接调用 | 不回收 | 可能 |
软引用 | 通过get()方法 | 视内存情况回收 | 不可能 |
弱引用 | 通过get()方法 | 永远回收 | 不可能 |
幻象引用 | 无法取得 | 不回收 | 可能 |
虚引用的使用,在netty的内存泄漏检测中使用到:ResourceLeakDetector中的ResourceLeak。
什么情况会导致索引失效
- 索引字段参与了运算 比如 where id/2 = 100; 应该改成 where id = 2*100;
- 以“%”开头的LIKE语句(%xxx,%xxx%这两种情况)
- OR语句前后没有同时使用索引 (AND就不会存在这种问题)
- 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型)
锁的优化策略
① 读写分离
② 分段加锁
③ 减少锁持有的时间
④ 多个线程尽量以相同的顺序去获取资源
等等,这些都不是绝对原则,都要根据情况,比如不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁
唯一索引和主键的区别
首先primary key跟unique都是Constraints,属于logical object,而index则是physical object,会占用index page并被保存在磁盘上。
- 主键一定会创建一个唯一索引,但是有唯一索引的列不一定是主键;
- 主键不允许为空值,唯一索引列允许空值;
- 一个表只能有一个主键,但是可以有多个唯一索引;
- 主键可以被其他表引用为外键,唯一索引列不可以;
- 主键是一种约束,而唯一索引是一种索引,是表的冗余数据结构,两者有本质的差别
B树和B+树
详见这两篇微信的文章吧
https://mp.weixin.qq.com/s/raIWLUM1kdbYvz0lTWPTvw
https://mp.weixin.qq.com/s/cK_GIhCuGoUwJpDpoaETxw
volatile作用
volatile的作用就是使得被其修饰的变量能够在多个线程中可见。
正常情况下,线程会各自从主内存拷贝一个当前变量值然后在自己的工作内存中操作,操作完成后写入到主内存。然后各线程工作内存再同步该变量在主内存中的新值,通过这样的流程实现变量值在多个线程中的可见。
JAVA会强制将标有volatile的变量修改后的新值立刻同步到主内存。当一个线程修改了变量的值,新的值会立刻同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。volatile规避了这么一种情况: 线程1修改了变量值后,在该变量的修改同步至主内存之前线程2就读取到了主内存的值…
为什么volatile关键字可以有这样的特性?这得益于java语言的先行发生原则(happens-before)。
缺点
- 不具有原子性,比如volatile修饰的i,多线程i++操作不能保证同步。
- 不能修饰方法
volatile防止指令重排
指令重排是指JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序。
指令重排的目的是为了在不改变程序执行结果的前提下,优化程序的运行效率。需要注意的是,这里所说的不改变执行结果,指的是不改变单线程下的程序执行结果。
volatile可以通过指令间加入内存屏障的方式防止指令重排。
http://blog.csdn.net/hqq2023623/article/details/51013468
synchronized 禁止重排序是通过内存屏障实现的, 简单来说, 内存屏障只保证指令不会越过该屏障, 而synchronized块内部的指令仍然有可能发生重排序。 所以还是得老老实实使用volatile。
参考:
https://mp.weixin.qq.com/s/EO5q7X1SI4WhsGUluIW_cA
https://mp.weixin.qq.com/s/Qjn8iRHoP7kfalYilJGx_Q
volatile和static在多线程下的区别
- volatile是告诉编译器,每次取这个变量的值都需要从主存中取,而不是用自己线程工作内存中的缓存.
- static 是说这个变量,在主存中所有此类的实例用的是同一份,各个线程创建时需要从主存同一个位置拷贝到自己工作内存中去(而不是拷贝此类不同实例中的这个变量的值),也就是说只能保证线程创建时,变量的值是相同来源的,运行时还是使用各自工作内存中的值,依然会有不同步的问题.
分布式和集群的区别
小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群。一个配菜师因故请假了,但是其余的配菜师还是该啥就干啥,可能没请假的配菜师任务会被均匀的加量了,但是他们的任务和职责是不变的
为什么要用到分布式
- 稳定业务和多变业务分离:比如转账业务不会变,但转账完成后续动作多变,可能发短 信,可能发邮件,还有别的奖励,也可能有个短期活动需要处理,如果每次变化都直接加到转账业务里,那就要频繁改动主业务,甚至需要重启,更甚至直接给主业物代码引入了新的bug
- 新业务老业务分离:一块成熟的业务,拓展出来一块新业务,新的业务只需要从老业务里面获取小部分数据或功能,剩下大部分都是很独立的逻辑,老业务模块根本不需要了解新业务模块,如果都堆积到老业务模块里面,笨重又混乱
- 团队隔离:核心业务系统由核心团队管理和维护,非核心业务系统由另外一个独立团队维护,互相暴露少量接口互通,互有边界控制
- 不同硬件需求:有的业务需要大内存,有的需要大硬盘,有的需要更强cpu,有的需要更多集群,根据不同需求分布到更适合的物理设备上,而不是统一配置爱用不用
- 还有更多理由需要分布式,即便是有先后顺序,也会把业务拆分成多个独立子系统,更多知识请百度“分布式”“微服务”“组件化”“模块化”
- 如果你的业务本来就很简单,拆分反而成了负担,也有拆分不合理后面重构合并到一起的
- “分布式架构”在一体化系统里面相对应的概念是“接口分离原则”,对应的问题就应该是“一个方法就能解决问题,为什么要分开好几个方法或接口”
作者:张鹏飞
链接:https://www.zhihu.com/question/20004877/answer/112124929
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
dubbo负载均衡策略:
在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。
-
RandomLoadBalance:
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。 -
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 -
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 -
ConsistentHashLoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
具体代码参见↓
如何创建线程/synchronized/wait/notify等的用法
创建线程的方法:
- extends Thread类 然后直接new. 这种需要setName来传入线程名称
- implements Runnable接口 然后作为Thread类的构造参数传入。这种可以在Thread的构造中直接传入线程名称。
- 通过Callable和Future创建线程 (需要线程返回值的时候)
synchronized的用法
这个关键字一言两语说不清。
大体有如下几种加锁方式吧
注意:
- synchronized不能加在abstract方法上,会直接编译错误
/**
* 测试加锁的几种方式
*
* 见main方法
* 结论:
* 1. 对整个方法上synchronized相当于对当前对象加锁
* 2. 类锁和对象锁互相不影响,可以同时执行
* 3. 同一个对象两个线程调用类锁互斥
*
* 既然可以利用join控制线程执行顺序
*/
public class ObjectLock {
public void method1(){
synchronized(this){ //对象锁
try {
System.out.println(Thread.currentThread().getName()+" do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){
synchronized(ObjectLock.class){ //类锁
try {
System.out.println(Thread.currentThread().getName()+" do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){
synchronized(lock){ //给定对象的锁
try {
System.out.println(Thread.currentThread().getName()+" do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void method4(){ //应该等同于method1
try {
System.out.println(Thread.currentThread().getName()+" do method4..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ObjectLock obj = new ObjectLock();
Thread t1 = new Thread(new Runnable(){
public void run(){
obj.method1(); //当前对象锁
// obj.method2(); //类锁
// obj.method3(); //外部对象锁
// obj.method4(); // 对整个方法加锁 相当于当前对象锁
}
},"T1");
Thread t2 = new Thread(new Runnable(){
public void run(){
// obj.method1(); //当前对象锁
// obj.method2(); //类锁
obj.method3(); //外部对象锁
// obj.method4(); // 对整个方法加锁 相当于当前对象锁
}
},"T2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//通过两个join保证了 t1 t2的先后执行的同时确保了main线程最后执行
System.out.println(Thread.currentThread().getName()+"-------END-------");
}
}
wait¬ify
这俩一般成对使用。主要用于线程之间的通信。
注意事项:
- wait和notify都必须在synchronized块中使用。
- wait会释放锁,意思就是当代码块执行到wait的时候此线程会停止,然后让出锁。
- notify会通知wait那端的线程。但是!!notify不会释放锁。意味着notify不能实时的结束去执行另一个线程,而是要等当前代码块执行完毕后才会启动wait那端的任务。所以,notify不要在循环里使用。
- wait编译时需要强制捕获异常,notify不需要。
- 调用的时候一定要指定对象,否则就是指this.wait和this.notify
暂时想这么多,待补充。。。
yield和sleep的区别
sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
不要假定高优先级的线程一定先于低优先级的线程执行,不要有逻辑依赖于线程优先级,否则可能产生意外结果。
线程优先级是个坑,所以可以默认为yield和sleep的区别就是“sleep可以指定参数,yield不行。”
notify和notifyall的区别
notify会唤醒一个线程,notifyall会唤醒当前等待的所有线程
java中的notify和notifyAll有什么区别? - Alex Wang的回答 - 知乎
https://www.zhihu.com/question/37601861/answer/94679949
1.cpu不高但是load值高,是怎么回事? 2.如果cpu高,你怎么找到问题所在
- load值高的原因是工作线程在等待其他线程让出cpu,但是cpu本身又不忙,说明有线程占着茅坑不拉屎,那么这种情况下,只有可能是线程阻塞了。
- cpu高,只需要用top找到占用cpu高的线程,然后把该线程的pid转化成16进制的id,用该id去jstack日志里看下,具体是哪段代码。
JAVA是值传递还是引用传递?
java中方法参数传递方式是按值传递。
如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。
详细的可以看看这个(我没怎么懂就是了)
https://www.zhihu.com/question/31203609
redo log和undo log
-
redo log:
redo log就是保存执行的SQL语句到一个指定的Log文件,当Mysql执行recovery时重新执行redo log记录的SQL操作即可。当客户端执行每条SQL(更新语句)时,redo log会被首先写入log buffer;当客户端执行COMMIT命令时,log buffer中的内容会被视情况刷新到磁盘。redo log在磁盘上作为一个独立的文件存在,即Innodb的log文件。 -
undo log:
与redo log相反,undo log是为回滚而用,具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer与redo buffer一样,也是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间),即使客户端设置了每表一个数据文件也是如此。
sleep和wait的区别
- sleep()方法是Thread的静态方法,而wait是Object实例方法
- wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
- sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。
sleep会让出CPU, wait会让出锁。
线程池的corePoolSize和maximumPoolSize的区别
当提交一个新任务到线程池时 首先线程池判断基本线程池(corePoolSize)是否已满?
没满,创建一个工作线程来执行任务。满了,则进入下个流程;
其次线程池判断工作队列(workQueue)是否已满?
没满,则将新提交的任务存储在工作队列里。
满了,则进入下个流程;
最后线程池判断整个线程池(maximumPoolSize)是否已满?
没满,则创建一个新的工作线程来执行任务,
满了,则交给饱和策略(handler)来处理这个任务;
如果线程池中的线程数量大于corePoolSize时,某线程空闲时间超过 keepAliveTime,该线程将被终止,直至线程池中的线程数目不大于 corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过 keepAliveTime,线程也会被终止。
static和final
static修饰的属性强调它们只有一个,final修饰的属性表明是一个常数(创建后不能被修改)。
static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问。
static final也可以修饰方法,表示该方法不能重写,可以在不new对象的情况下调用
从表中随机取N条数据
select * from table_name where rand() limit n;
这样就会从表中随机取出n条数据。
另外说一下 rand()
会返回一个介于0到1之间的随机浮点数。每一次执行都会产生不同的随机数值。
如果写rand(5)
(里面的5可以替换成别的值) ,那么每一次执行产生的随机数值都是相同的。。。
方法的重载和重写
重载:
同一个方法名,不同的参数列表。构成重载
注意点:
- 与返回值类型无关,即int A()和void A()同时存在会报错,不能构成重载。
- 与修饰方法的关键字无关,即 static void A()和 void A()不能构成重载。
void A(int a...){
}
void A(String x...){
}
void A(){
}
//以上三个方法构成重载
另外看下面的代码
public class Test {
public static void main(String[] args) {
Son p = new Son(); //打印Son
// Person p = new Son(); //打印Person
// Person p = new Person(); //打印Person
// Object p = new Person(); //打印Object
A(p);
}
public static void A(Object a){
System.out.println("Object");
}
public static void A(Person p){
System.out.println("Person");
}
public static void A(Son s){
System.out.println("Son");
}
}
class Person{
}
class Son extends Person{
}
重写:
方法的重写首先是建立在继承(extends)上的,没有继承就不存在重写。
重写是发生在子类的一种行为,是对子类从父类继承过来的方法的一种扩展和丰富。如果不重写的话默认使用的是父类的方法。
@Override
重写必须保证方法名称,参数类型和个数,返回值类型全部相同。
注意事项:
- 子类重写方法的访问权限一定要大于父类。就是比父类更open。参考public>protected>不加>private … 但是!父类如果是private,子类不能重写该方法。相当于重新定义了一个方法
- static修饰的方法无法被重写,想想看啊static的方法直接用类名调用。。
- 子类在重写父类抛出异常的方法时,要么不抛出异常,要么抛出与父类方法相同的异常或该异常的子类。
public/protected/default/private
访问修饰符 | 自身 | 同包 | 父子 | 非同包非父子 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
default | √ | √ | ||
private | √ |
throwable/exception/error/runtimeException
如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mr3x63bP-1571844072148)(http://note.youdao.com/yws/res/31315/WEBRESOURCEee04e6e6f246b16276b80cff70a648fe)]
该图有些不严谨,图中Exception的分支IOException那里代指的是检查型异常,这里需要注意一下。
参考
https://blog.csdn.net/hl_java/article/details/76837141
Spring AOP
AOP,一般称为面向方面(切面)编程,作为面向对象的一种补充,用于解剖封装好的对象内部,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法,当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。
②如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
Spring的IoC理解
(1)IOC就是控制反转。就是对象的创建权反转交给Spring,由容器控制程序之间的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。(依赖)控制权由应用代码本身转到了外部容器,由容器根据配置文件去创建实例并管理各个实例之间的依赖关系,控制权的转移,是所谓反转,并且由容器动态的将某种依赖关系注入到组件之中。BeanFactory 是Spring IoC容器的具体实现与核心接口,提供了一个先进的配置机制,使得任何类型的对象的配置成为可能,用来包装和管理各种bean。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring就是根据配置文件在运行时动态的去创建对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :
第一是根据属性注入,也叫set方法注入;
第二种是根据构造方法进行注入;
第三种是根据注解进行注入。
详细的说:
(4)IoC,控制反转:将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化以及装配好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。就是将对象的控制权反转给spring容器管理。
(5)DI机制(Dependency Injection,依赖注入):可以说是IoC的其中一个内容,在容器实例化对象的时候主动的将被调用者(或者说它的依赖对象)注入给调用对象。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
Spring的优点
(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制降低了业务对象替换的复杂性;
(3)容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能;
(4)降低了组件之间的耦合性 ,实现了软件各层之间的解耦;
(5)容器提供单例模式支持;
(6)可以使用容器提供的众多服务,如事务管理,消息服务等;
(7)容器提供了众多的辅助类,能加快应用的开发;
(8)spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
(9)独立于各种应用服务器
(10)Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部。
红黑树
- 红黑树的初衷是为了解决二叉查找树在特定情况下不平衡问题,红黑树插入和删除时会维护平衡,即自平衡 (简而言之查找快又平衡)
- 红黑树的应用: TreeMap, TreeSet 以及jdk8以后的HashMap(单个entry链表追加超过8时转化为红黑树)
具体红黑树的讲解 什么是红黑树?
bean的生命周期
- 实例化Bean对象
- 设置Bean属性
- 如果我们通过各种Aware接口生命了依赖关系,则会注入Bean对容器基础设施层面的依赖。具体包括BeanNameAware、BeanFactoryAware和ApplicationContextAware,分别会注入Bean ID、Bean Factory或者ApplicationContext。
- 调用BeanPostProcessor的前置初始化方法postProcessBeforeInitialization。
- 如果实现了InitializingBean接口,则会调用afterPropertiesSet方法。
- 调用Bean自身定义的init方法。
- 调用BeanPostProcessor的后置初始化方法postProcessAfterInitialization。
- 创建过程完毕
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4KnKFW2-1571844072149)(http://note.youdao.com/yws/res/31458/WEBRESOURCEa128211d74e9954a289a365c4ae29ac7)]
什么时候引发的初始化呢?什么时候触发销毁操作呢?spring容器管理的bean是在容器运行过程中不会被销毁吧?
首先理解scope:
- Singleton(单例) 在整个应用中,只创建bean的一个实例
- Propotype(原型) 每次注入或者通过Spring应用上下文获取的时候,都会创建一个新
的bean实例。 - Session(会话) 在Web应用中,为每个会话创建一个bean实例。
- Request(请求) 在Web应用中,为每个请求创建一个bean实例。
他们是什么时候创建的:
1一个单例的bean,而且lazy-init属性为false(默认),在Application Context创建的时候构造
2一个单例的bean,lazy-init属性设置为true,那么,它在第一次需要的时候被构造.
3 其他scope的bean,都是在第一次需要使用的时候创建
他们是什么时候销毁的:
1 单例的bean始终 存在与application context中, 只有当 application 终结的时候,才会销毁
2 和其他scope相比,Spring并没有管理prototype实例完整的生命周期,在实例化,配置,组装对象交给应用后,spring不再管理.只要bean本身不持有对另一个资源(如数据库连接或会话对象)的引用,只要删除了对该对象的所有引用或对象超出范围,就会立即收集垃圾.
3 Request: 每次客户端请求都会创建一个新的bean实例,一旦这个请求结束,实例就会离开scope,被垃圾回收.
4 Session: 如果用户结束了他的会话,那么这个bean实例会被GC.
Spring线程安全性
Spring框架中的单例Beans是线程安全的么?
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
10、Spring如何处理线程并发问题?
Spring使用ThreadLocal解决线程安全问题。
我们知道在一般情况下,只有有状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
(1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
(2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
(3)概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
Spring自动装配模式
(1)no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
(2)byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
(3)byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
(4)constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
(5)autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式。
Spring控制器加载过程
(xml版)
(1)Web容器创建;
(2)上下文创建,但未初始化;
(3)监听器创建,并注册到Context上;
(4)上下文初始化;
(5)通知到监听者,Spring配置文件/@Configuration加载;
(6)Load-on-startup>0的ServletConfig创建,springMVC的DispatcherServlet此时创建。
PS:Spring容器时SpringMVC的父容器。Spring的AOP在Spring的上下文创建时就会创建;如果想要代理SpringMVC的控制层,需要将配置写到SpringMVC的配置文件下。
Spring用了什么设计模式
Spring 框架中都用到了哪些设计模式?
(1)代理模式—在AOP和remoting中被用的比较多。
(2)单例模式—在spring配置文件中定义的bean默认为单例模式。
(3)工厂模式—BeanFactory用来创建对象的实例。
(4)模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)前端控制器—Spring提供了DispatcherServlet来对请求进行分发。
(6)视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
(7)依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。
Spring事物种类和各自区别
spring支持编程式事务和声明式事务两种方式:
(1)编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
(2)声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
(3)显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
spring的事务传播行为
spring事务的传播行为说的是当一个方法调用另一个方法时,事务该如何操作。
(1)PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
(2)PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
(3)PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
(4)PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
(5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
(6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
(7)PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
事物的实现方式和实现原理
(1)划分处理单元——IOC:
由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用spring中的IOC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。
(2)AOP拦截需要进行事务处理的类:
Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。
读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。
(3)对事物处理实现(事务的生成、提交、回滚、挂起):
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。为常用数据源支持提供了一系列的TransactionManager。
(4)结合:
PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。
Spring AOP里面的几个名词
(1)切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
(2)连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。
(3)通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。
(4)切入点(Pointcut):匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
(5)引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。
Spring通知有哪些类型
(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
Spring核心接口
BeanFactory和ApplicationContext是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的容器,生成Bean实例的,并管理容器中的Bean。
(1)BeanFactory:是Spring里面最底层的接口,提供了最简单的容器的功能,负责读取bean配置文档,管理bean的加载与实例化,维护bean之间的依赖关系,负责bean的生命周期,但是无法支持spring的aop功能和web应用。
(2)ApplicationContext接口作为BeanFactory的派生,因而具有BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,以一种更面向框架的方式工作以及对上下文进行分层和实现继承,相较于BeanFactorty,ApplicationContext还提供了以下的功能:
①默认初始化所有的Singleton,也可以通过配置取消预初始化。
②继承MessageSource,因此支持国际化。
③资源访问,比如访问URL和文件。
④事件机制。
⑤同时加载多个配置文件。
⑥以声明式方式启动并创建Spring容器。
⑦载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
(3)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
(5)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
用Spring做过什么
暂等
Spring bean的作用域之间有什么区别?
Spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下:
singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
全局作用域与Servlet中的session作用域效果相同。
BTREE索引和HASH索引的区别
首先,MySQL中的BTREE索引使用的是B+树。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfFt32Ps-1571844072150)(https://note.youdao.com/yws/res/11562/4EC7C2188B4B400782D7DE41B55387BF)]
- 在数据量大且数据重复度很低,且仅有等值查询(xxx = value)的情况下使用HASH索引
- 其他情况下,存在着范围排序分组的,都使用BTREE索引
两者区别
- 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
- BTREE索引更适合范围查询,HASH索引在此时就毫无用处。BTREE索引使用的B+树先天的就会将数据进行排序,并且父节点划分了范围。而经过HASH算法后的数据都被打散了,HASH索引没法利用范围检索
- 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询)
- 哈希索引也不支持多列联合索引的最左匹配规则
- B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在哈希碰撞。
那么什么时候使用HASH索引呢?
在MySQL中,只有HEAP/MEMORY引擎表才能显式支持哈希索引(NDB也支持,但这个不常用),InnoDB引擎的自适应哈希索引(adaptive hash index)不在此列,因为这不是创建索引时可指定的。
还需要注意到:HEAP/MEMORY引擎表在mysql实例重启后,数据会丢失。
通常,B+树索引结构适用于绝大多数场景,像下面这种场景用哈希索引才更有优势:
SELECT ... FROM t WHERE c1 = ?; -- 仅等值查询
ReadLock&WriteLock
这俩都是共享锁,区别于独占锁。
ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
ReadLock r = lock.readLock();
WriteLock w = lock.writeLock();
两者都有lock,unlock方法。写写,写读互斥,读读不互斥。
可以实现并发读的高效线程安全代码
一句话说清楚什么叫可重入锁
所谓的可重入锁,指的是你某个线程A已经获得了锁,然后在线程A释放锁之前线程A还能再次获得这个锁。
不可重入就是,如果线程A获得了锁且没释放,那么包括这个线程在内的所有线程都不可以获得这把锁。
可以举一个帮助理解可重入锁的例子,比如往数据库数据库插入一条记录,然后利用数据库记录的不可重复性形成一把锁。这把锁就是不可重入锁,因为无论是哪个线程也无法在这条数据删除之前(释放锁)再次获得锁(插入该记录)。当然可以利用数据库实现可重入锁,比如记录线程ID等方式
synchronized关键字是如何实现可重入的呢?
每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。
双亲委派机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugLK6fFg-1571844072150)(http://note.youdao.com/yws/res/31603/WEBRESOURCEf95b4a8f6207849f5f998a49e7b27d04)]
Java中的类加载器大致可以分为两类,一类是系统提供的,另外一类则是由Java应用开发人员编写的。系统提供的类加载器主要有下面三个:
- 引导类加载器 (bootstrap class loader):它用来加载Java的核心库(比如java.lang目录下的),是用原生代码实现的,并不继承自java.lang.ClassLoader。
- 扩展类加载器 (extensions class loader):它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。
- 系统类加载器 App ClassLoader(SystemClassLoader):它根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来或其它。
注意,这三者不是继承关系。
BootStrapClassLoader不是ExtClassLoader的超类,ExtClassLoader也不是AppClassloader的超类。
ExtClassLoader可以由AppClassLoader通过getParent的方式来获得。而BootStrap无法通过getParent的方式获得,getParent只会返回null
类的关系如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76hJMXLU-1571844072151)(http://note.youdao.com/yws/res/31721/WEBRESOURCEd440a621cf7cc01ebeffaf44b29844dc)]
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
- 如果不想打破双亲委派模型,那么只需要重写findClass方法即可
- 如果想打破双亲委派模型,那么就重写整个loadClass方法
实现类加载器
既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?
因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。
定义自已的类加载器分为两步:
- 继承java.lang.ClassLoader
- 重写父类的findClass方法
这里可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?
因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。
自己实现的类加载器无法加载java核心类库的类。