varchar和nvarchar区别
1、varchar是非Unicode编码,nvarchar是Unicode编码;
2、varchar的最大值是8000,nvarchar是4000;
3、varchar能存储的字节数是它的长度,nvarchar能存储的字节数是它长度的2倍;
4、varchar比nvarchar更省空间。
表单重复提交解决办法?
前端:设置按钮只触发一次,提交按钮变灰。
后端:使用token+session
第一步,用户调用后台接口;
第二步,后台生成sessionToken传到前端;
第三步,前端将sessionToken传到后台;
第四步,后台将第一次生成的sessionToken和前端传来的sessionToken进行对比,如果一致就可以提交表单,不一致就表示表单重复提交了。
mysql大数据量存储
按规则进行分库分表
JDK1.8中HashMap的变化
1.8之前:HashMap处理“碰撞”的时候,都采用链表来存储,当碰撞的节点很多时,查询时间是O(n).
1.8中:HashMap处理“碰撞”增加了红黑树数据结构,当碰撞节点较少时,采用红黑树存储,当较大时(>8个),采用红黑树存储。
Redis缓存的优势
1、读写性能很高;
2、redis支持多种数据类型。存储的是key-value格式的数据,其中key是字符串,value的数据类型有String、list、set、有序集合zset(sorted set);
3、非关系型数据库;
4、持久化:redis的持久化方式有rdb和aof两种;rdb是将内存中的数据压缩后写入到硬盘上,aof是将日志整理写到硬盘中,并设置不同的持久化方式,有效的额减轻了服务器的压力,同时在很大程度上防止了数据的丢失。
SpringCloud
组件:
Eureka(注册中心):各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里。由服务端和客户端组成;服务端:做服务注册中心,客户端:用来处理服务注册与发现;在应用启动时,客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地,客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。
Ribbon(客服端负载均衡):服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台。
Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
Hystrix(断路器):起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Netflix Zuul(服务网关):如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务。
Spring Cloud Config(分布式配置):配置管理,提供服务器端和客户端。
详细讲解可以查看:https://blog.youkuaiyun.com/yeyazhishang/article/details/84344402
表结构及索引优化
1、分库分表、读写分离;
2、为字段选择合适的数据类型;
3、将字段多的表分解成多个表,增加中间表;
4、混用范式和反范式,适当冗余;
5、为查询创建必要索引,但避免滥用;
6、列字段尽量设置为not null。
SQL优化
1、寻找最需要优化的语句:分析满查询日志;
2、利用分析工具:explain、profile;
3、避免使用select * ,只取需要的列;
4、使用索引扫描排序;
5、避免where子句对字段进行null值判断;
6、避免where子句使用 !=、<>操作符和or连接条件;
7、连续的数值,能用between就不要用in
建表三范式:原子性、主键唯一性、减少表与表之间的字段冗余
补充:
创建索引注意事项
1、增删改操作较多的表不要创建索引;
2、不要在大的文本字段或超长字段建立索引;
3、不要在取值朝一个方向增加的字段(如:日期)上创建索引
索引何时失效
1、like未使用最左前缀,如where A like "%China";
2、搜索一个索引而在另一个索引上做order by;
3、or会使索引失效;
4、若列是字符串类型,要使用引号
JDBC连接数据库的步骤
1、加载驱动;
2、创建数据库连接;
3、创建Statement对象,执行SQL语句;
4、获取结果集,遍历结果集;
5、关闭资源,关闭数据库。
Servlet生命周期
首先:加载servlet的class,实例化servlet;
然后:初始化servlet调用init()方法;
其次:调用服务的service的方法处理doGet()和doPost()方法;
最后:关闭容器时调用destroy()销毁方法。
JAVA特性
封装:将对象的属性私有化,对外提供get,set方法,隐藏具体的实现
继承:子类继承父类的属性和方法,并根据需要扩展新的属性和方法,提高了代码的复用性
多态:一种事物的不同表现形态。例如:重写和重载
Throwable
Throwable继承自Object类,子类主要是Error和Exception类。
Error:程序无法处理的错误,只能重启系统。
Exception:运行时异常和非运行时异常;
运行时异常:只有运行时才会出错,所以不需要try catch捕获,由虚拟机帮我们捕获;
非运行时异常:写代码的时候出错会提醒我们try catch或throws;
常见的运行时异常:
1、ClassCastException;
2、ClassNotFoundException;
3、IndexOutBoundsException;
4、NullPointerException;
5、ArrayStoreException;
6、SQLException;
7、ArithmeticException;
8、NumberFormatException
常见的非运行时异常:
1、IOException;
2、FileNotFoundException
Nginx(负载均衡器)
是一个高性能的WEB服务器和反向代理服务,最常用的软件负载均衡器。(网易、淘宝、腾讯、新浪等等互联网公司)
核心概念:用户请求先到Nginx,再由Nginx转发请求到后面的应用服务器(如:Tomcat)
Nginx常用作Http反向代理(接收和转发请求)
Http请求的处理包括解析和封装Http内容
负载均衡算法:1、轮循均衡,2、权重轮循均衡,3、随机均衡
LVS-负载均衡器
核心概念:原本是请求LVS服务器的数据包,被LVS软件篡改了数据包的目的地,将流量转移到了Nginx所在的机器IP,从而实现负载均衡
LVS属于七层协议中的第四层-传输层,使用的协议(如TCP)内容比Http简单,解析和组装所消耗的CPU、内存等资源比Nginx要低,所以LVS比Nginx快,另外Nginx支持1W~10W并发,而LVS支持10W~50W并发
常用组合:Nginx+Tomcat或者LVS+Nginx+Tomcat
Dubbo
dubbo是一个由阿里巴巴开源的、分布式的RPC(Remote Procedure Call Protocol-远程过程调用)和微服务架构,现为Apache顶级项目
Dubbo提供了三个关键功能:
- 基于接口的远程调用;
- 容错和负载均衡;
- 服务自动注册与发现
Dubbo结构图:
Container:服务运行容器;
Provider:暴露服务的服务提供方;
Registry:服务注册与发现的注册中心;
Consumer:调用远程服务的服务消费方;
Monitor:统计服务的调用次数和调用时间的监控中心
SpringMVC
请求处理流程图:
①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件.
②:HanlerMapping是springmvc中完成url到controller映射的组件.DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.
③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件.
④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端.
线程
实现线程的三种常用方法:继承Thread、实现Runnable或Callable接口
多线程实现同步的方法:synchronized关键字、wait()和notify()方法、Lock
具体实现:
public class MyThread extends Thread{
public void run(){//重写run方法
//需要执行的线程体
}
}
public class MyThread implements Runnable{
public void run(){//重写run方法
//需要执行的线程体
}
}
public class MyThread implements Callable{
public int call(){//重写call方法,可有返回值,可抛出异常
//需要执行的线程体
}
}
两个常用的方法:sleep()和wait()
sleep():线程的方法,休眠不释放锁,到时间自动唤醒;
wait():Object的方法,会释放锁,需要通过调用notify()或notifyAll()唤醒
多线程的5种状态
- 新建:新建线程;
- 就绪:调用start()方法并有返回;
- 运行:获取CPU,调用run()方法;
- 阻塞:1、sleep()休眠,2、线程要获取同步锁,而该锁正在被其他线程占用
- 死亡:1、run()正常运行结束,2、exception,3、error
死锁的产生:两个或两个以上线程执行中,因争夺资源产生的一种互相等待的现象;例如:小明有笔,小红有本;小明拿着笔等小红把本给他,小红拿着本等小明把笔给她
解决办法:
第一种:代码避免
通过构造方法共享数据,并使用(等待-唤醒)机制实现线程通信。
等待唤醒机制:A:生产者-先看是否有数据,有就等待,没有就生产,然后通知消费者消费;
B:消费者-先看是否有数据,有就消费,消费完通知生产者生产,没有就等待
第二种:信号量
通过信号量设置线程的访问数量,当一个线程获取到锁后,会有一个时间限制,当时间耗尽时,该线程就会释放锁由其他线程获取
设计模式
1.单例:饿汉式和懒汉式
饿汉式
public class Singleton{
private static Singleton single = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return single;
}
}
懒汉式
public calss Singleton{
private static Singleton single;
private Singleton(){}
public static Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
}
//Double checked 双重检查 线程安全
public class Singleton{
private static volatile Singleton single;
private Singleton(){}
public static Singleton getInstance(){
if(single == null){
synchronized(Singleton.class){
if(single == null){
single = new Singleton();
}
}
}
return single;
}
}
懒汉式:第一次使用实例的时候创建;
饿汉式:在加载类的时候就会创建类的单例,并保存在类中。
2.装饰设计模式
概念:通过“装饰”一个类,增强它的功能。例如:买一辆车,想为新车装一个倒车雷达,这就相当于为这辆汽车增加新的功能。
应用:IO流中字节缓冲流(BufferedInputStream和BufferedOutputStream)
3.工厂模式
应用:spring容器(IOC)中对象的管理应用了工厂模式,对象的生成应用了单例模式;
AOP:动态代理(JDK、CGLIB)、静态代理(静态织入)
算法
冒泡排序:
int[] a = {5,2,8,6,4,9};
for(int i=0;i<a.length;i++){
for(int j=0;j<a.length-i-1;j++){
if(a[j]>a[j+1]){
int temp = a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
快速排序:
public static void sort(int a[], int low, int hight) {
int i, j, index;
if (low > hight)return;
i = low;
j = hight;
index = a[i]; // 用子表的第一个记录做基准
while (i < j) { // 从表的两端交替向中间扫描
while (i < j && a[j] >= index)
j--;
if (i < j)
a[i++] = a[j];// 用比基准小的记录替换低位记录
while (i < j && a[i] < index)
i++;
if (i < j) // 用比基准大的记录替换高位记录
a[j--] = a[i];
}
a[i] = index;// 将基准数值替换回 a[i]
sort(a, low, i - 1); // 对低子表进行递归排序
sort(a, i + 1, hight); // 对高子表进行递归排序
}
public static void quickSort(int a[]) {
sort(a, 0, a.length - 1);
}
public static void main(String[] args) {
int a[] = { 49, 38, 65, 97, 76, 13, 27, 49 };
quickSort(a);
System.out.println(Arrays.toString(a));
}
二叉树:
排序规则:前序遍历(根、左、右),中序遍历(左、中、根),后序遍历(左、右、根)
遍历结果如下:
前序遍历:ABCDEFGHK
中序遍历:BDCAEHGKF
后序遍历:DCBHKGFEA
TCP和UDP
TCP:面向连接,传输的数据量大,可靠,传输比较慢(三次握手)
UDP:面向非连接,传输数据量小,不可靠,传输比较快
TCP占包问题:
原因:数据发送频繁
解决方法:1、发送固定长度数据
2、把数据大小与数据同时发送
3、用特殊标记进行标记
Servlet九大内置对象
request:请求对象
response:响应对象
page:页面对象
exception:例外对象
pageContext:页面上下文对象
session:会话对象
out:输出对象
application:应用程序对象
config:配置对象
zookeeper
集群的搭建原则:容灾原理(创建奇数台)
应用:名字服务、配置管理、提供分布式同步和集群管理
数据类型
1、基本数据类型:
byte:1字节
short:2字节
int:4字节
long:8字节
char:2字节
float:4字节
double:8字节
boolean
注:1字节=8bit(位)
2、引用数据类型:
类:class
接口:interface
数组:[ ]
sql语句插入一条记录同时获取刚插入的id
使用output关键字输出,inserted为已插入的临时表
例如:insert into student(name,studentno,sex,age)
output inserted.id values("张三","10000",'男',22)
集合
Connection接口:
List:有序,可重复性
- ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高 - Vector
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低 - LinkedList
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
Set:无序,不可重复
- HashSet
- 底层数据结构是哈希表。(无序,唯一)
- 如何来保证元素唯一性?
- 1.依赖两个方法:hashCode()和equals()
- LinkedHashSet
- 底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
- 1.由链表保证元素有序
- 2.由哈希表保证元素唯一
- TreeSet
- 底层数据结构是红黑树。(唯一,有序)
- 1. 如何保证元素排序的呢?
- 自然排序
- 比较器排序
Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
- TreeMap是有序的,HashMap和HashTable是无序的。
- Hashtable的方法是同步的,HashMap的方法不是同步的。
- Hashtable是线程安全的,HashMap是非线程安全的。
- Hashtable效率低,HashMap效率高。
- LinkedHashMap记录了数据插入顺序,TreeMap能够把保存的记录根据键排序
HashMap底层
1、什么是HashMap?
HashMap接受null键null值,但Hashtable不能;
HashMap是非线程安全的,但效率快;
HashMap储存的是键值对
2、工作原理
HashMap是基于hashing原理,我们使用put(key,value)储存对象到HashMap中,使用get(key)从HashMap中获取对象;当我们给put()方法传递键和值时,我们先调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象
详细讲解可参考:https://blog.youkuaiyun.com/suifeng629/article/details/82179996
补充:
ConcurrentHashMap:采用了锁分段技术保证线程安全。
锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
红黑树特性:1、每个节点或是黑色,或是红色;
2、根节点是黑色;
3、每个叶子节点是黑色;
4、如果一个节点是红色的,则它的子节点必须是黑色的;
5、从一个节点到该节点的子孙节点的所有路径上包含相同数目黑节点数。
求1-100之间的素数
public static void main(String[] args) {
System.out.println(2);
for(int i=2;i<100;i++){
if(i%2!=0){//去除能被2整除的数
if(getNum(i)){
System.out.println(i);
}
}
}
}
//素数
private static boolean getNum(int i) {
for(int j=2;j<i;j++){
if(i%j==0){
return false;
}
}
return true;
}
统计一个字符串中字符出现的次数并打印出来
public static void main(String[] args) {
String s = "abccbasdfwwdd";
char[] c = s.toCharArray();
Map<Character,Integer> map = new HashMap<Character,Integer>();
for (char d : c) {
if(map.containsKey(d)){
map.put(d, map.get(d)+1);
}else{
map.put(d, 1);
}
}
for (Entry<Character,Integer> en : map.entrySet()) {
System.out.println(en.getKey()+":"+en.getValue());
}
}
删除list集合中的重复元素
String[] arr = {"aa","bb","cc","aa","dd","cc"};
Arrays.sort(arr);
List<String> list = new ArrayList<String>();
for (String i : arr) {
if(!list.contains(i))
list.add(i);
}
System.out.println(list);
输出结果:[dd, aa, bb, cc]
MyBatis的一级缓存和二级缓存
一级缓存:sqlSession级别,自动开启
二级缓存:mapper级别,手动开启(配置),开启二级缓存后,需要将缓存的pojo实现序列化
Spring和MyBatis整合时,每次查询后要关闭sqlSession,关闭后数据会被清空,所以Spring整合后,若无事务,一级缓存无意义,开启二级缓存后,会把sqlSession一级缓存中的数据添加到二级缓存。
总结:查询多commit少,且对查询实时性要求不高,采用MyBatis二级缓存降低数据库访问量,提高查询效率。
二级缓存应用场景:对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。mybaits的二级缓存区域以mapper为单位划分的。多个sqlSession可以共享一个mapper中的二级缓存区域。
JVM体系结构
补充:
JVM用永久代(PermanetGeneration)来存放方法区。
JVM分别对新生代和旧生代采用不同的垃圾回收机制:
新生代:复制算法;
老年代:标记-清除算法
堆和方法区是所有线程共有的,堆内存可分为:新生代和老年代
JVM GC
范围:只回收堆区和方法区内的对象
回收判定:1、对象没有引用
2、程序在作用域正常执行完毕
3、程序调用System.exit()
4、程序发生意外终止(被杀线程等)
Minor GC:最新创建的对象在新生代区域中很快变得不可达,然后消失的过程
Full GC:对象从老年代区域消失的过程
生产者消费者模式解决了哪些问题
1、解耦
2、支持并发
Http
1请求:
请求头:
1. 请求方式(Post、Get)
2. url
3. 协议:http/1.0,2.0
4. 头参数:host,cookie,connection,user-Agent
请求体:Post(有),Get(无)
2响应:
1. 响应类型:文字、图片等
2. 长度
3. 状态码:200(成功),302(在其他地址发现请求),403(禁止访问),404(资源不存在),500(服务器内部错误)
4. 响应体:代码
3状态:
cookie,session 连接状态
4安全/攻击
附加:
1开头:信息,服务器收到请求;
2开头:成功;
3开头:重定向,要进一步操作完成请求;
4开头:客户端错误,请求包含语法错误或无法完成请求;
5开头:服务器错误,服务器在处理请求的过程中发生了错误
http和https的区别:http://www.360doc.com/content/18/0710/19/44130189_769384412.shtml
过滤器
过滤器应用到了责任链模式
多个过滤器的执行顺序是按web.xml的配置顺序执行的。
IO流
1、对象字节输入流和输出流
写入文件对象信息
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("文件路径")));
output.writeObject(new User("张三",10));
output.close();
读取文件对象信息
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("文件路径")));
User user = (User) input.readObject();
System.out.println(user);
input.close();
2、缓冲字节输入流和输出流
遍历文件夹,并将文件夹下的所有文件复制到指定的位置
public static void main(String[] args) throws IOException {
recursion("文件夹路径");
}
private static void recursion(String path) throws IOException {
File file = new File(path);
if(file.exists() && null != file && file.length()!=0){
File[] files = file.listFiles();
for (File file2 : files) {
if(file2.isDirectory()){
recursion(file2.getAbsolutePath());
}else{
try {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file2.getAbsoluteFile()));
File file3 = new File("文件夹路径",file2.getName());
File file4 = new File("文件夹路径");
if(!file4.exists())file4.mkdir();
if(!file3.exists())file3.createNewFile();
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file3));
int len;
while((len=in.read())!=-1){
System.out.println(len);
out.write(len);
}
in.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
3、转换流
Java统计一个字符串在另一个字符串中出现的次数
第一种方法:
public static void main(String[] args) {
String a = "718679asdas718asasasc21133718";
String b = "718";
filter(a, b);
}
public static void filter(String a,String b){
int count = 0;
while(true){
int i = a.indexOf(b);
if(i==-1)break;
count ++;
a = a.substring(i+b.length());
}
System.out.println(count);
}
第二种方法:
public static void main(String[] args) {
String a = "718679asdas718asasasc21133718";
String b = "718";
filter(a, b);
}
public static void filter(String a,String b){
String replace = "";
if(a.contains(b))
replace = a.replace(b,"");
int count = (a.length()-replace.length())/b.length();
System.out.println(count);
}
float f = 3.4 正确吗?
不正确,精度不准确,应该用强制类型转换,如:float f = (float )3.4 或 float f = 3.4f
在java里,无小数点的默认是int,有小数点的默认是double
ArrayList的扩充问题?
ArrayList list = new ArrayList();这种是默认创建大小为10的数组,每次扩容大小为1.5倍
ArrayList list = new ArrayList(20);使用的是ArrayList的有参构造函数,创建时直接分配其大小,没有扩充
Thread类常用方法
这里列举几个
- start()启动线程
- getId()获得线程ID
- getName()获得线程名字
- getPriority()获得优先权
- getState()获得线程状态
- sleep()休眠线程
- interrupt()中断线程
- currentThread()获得正在执行的线程对象
Maven的功能
- 通过maven内置的Tomcat启动项目;
- 利用maven直接把maven项目打成war包;
- 使用maven对项目进行热部署
内存泄漏和内存溢出
内存溢出:out of memory,指程序在申请内存时,没有足够的内存空间供其使用
内存泄漏:memory leak,指程序在申请内存后,无法释放已申请的内存空间
补充:
JVM内存溢出原因:
- 内存中加载的数据量过大;
- 代码中存在死循环;
- 集合类中有对对象的引用,使用完未清空,使JVM不能回收;
- 启动参数内存值设定过小
存在使 i + 1 < i 的数吗?
存在,若 i 为 int 整型,那么当 i 为 int 能表示的最大整数时, i + 1 就溢出变成负数了
对登录表单的密码如何进行加密?
RSA非对称算法:使用RSA会产生公钥和私钥,基于产生的公钥对明文加密,针对已经加密的明文,使用私钥解密,得到明文
分布式锁的实现方式
这里列出三种:基于数据库实现分布式锁、基于缓存(Redis等)实现分布式锁、基于Zookeeper实现分布式锁
第一种【基于数据库实现分布式锁】:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁
第二种【基于Redis的实现方式】:
- 获取锁的时候,使用set加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断;
- 获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁;
- 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
选取原因:Redis有很高的性能;Redis命令对此支持性好,实现起来比较方便
补充说明:
加锁代码:
jedis.set(String key, String value, String nxxx, String expx, int time);
第一个为key,我们使用key来当锁,因为key是唯一的。
第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
第五个为time,与第四个参数相呼应,代表key的过期时间。
第三种【基于Zookeeper实现分布式锁】:
- 创建一个目录mylock;
- 线程A想获取锁就在mylock目录下创建临时顺序节点;
- 获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
- 线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
- 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。
数据库连接池
使用了连接池可以带来诸多便利,下面列出几点主要的供大家看:
- 资源重用:由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
- 更快的系统响应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
- 新的资源分配手段:对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
- 统一的连接管理,避免数据库连接泄漏:在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。
想对数据库连接池做更深入的学习,可以点击下面链接:https://cloud.tencent.com/developer/news/312412
md5加密、 3-DES加密、 base64编码、 URLEncoder编码
- md5加密,该加密算法是单向加密,即加密的数据不能再通过解密还原。相关类包含在java.security.MessageDigest包中。
- 3-DES加密,该加密算法是可逆的,解密方可以通过与加密方约定的密钥匙进行解密。相关类包含在javax.crypto.*包中。
- base64编码,是用于传输8bit字节代码最常用的编码方式。相关类在sun.misc.BASE64Decoder 和sun.misc.BASE64Encoder 中。
- URLEncoder编码,是一种字符编码,保证被传送的参数由遵循规范的文本组成。相关类在java.net.URLEncoder包中。
synchronized
在JVM中,对象在内存中的布局分为三块区域:对象头、实例变量、填充数据。
java对象头是实现synchronized锁对象的基础。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor, 然后再执行方法,最后再方法完成时释放monitor。
补充:如何保证多线程读写数据时数据的一致性?
同步:使用synchronized关键字,或使用锁对象;
使用volatile关键字,它能使变量在值发生改变时尽快让其他线程知道。
区别:1、volatile能实现变量的修改可见性,但不具备原子性;
2、volatile使用在变量级别,synchronized可使用在变量和方法;
3、volatile不会造成线程阻塞,synchronized可能会造成线程阻塞。
分布式微服务化架构
- 容易开发和实现
- 经常发布,部署复杂
- 响应时间慢,吞吐量大
- 扩展性好
- 模块化后,重用性变高
Linux常用命令
创建文件夹:mkdir abc(文件夹名)
打包并压缩gzip:tar -zcvf abc.tar.gz abc
解压:tar -zxvf abc.tar.gz
创建空的文件:touch a(文件名)
删除文件夹所有文件:rm -rf abc
删除空文件夹:rmdir abc
删除文件:rm a
查询文件:在查询的文件夹下才能查找 find a
查看日志:tail -f a(文件名)
查看指定行数:tail -fn 10 a
查看文件所有内容:tail a
查看端口占用情况:lsof -i:8080
查看端口进程情况:netstat -tunlp |grep 8080
磁盘占用情况:du -sh abc
服务器内存使用情况:free
查看当前所有程序用户的线程:ps -ef
查看tomcat当前线程情况:ps -ef|grep tomcat
某个进程使用情况:top | grep 'xxx'
查看cpu情况:cat /proc/cpuinfo
推荐:https://blog.youkuaiyun.com/xinzhifu1/article/details/59109792/
Spring事务
特性:原子性、一致性、隔离型、持久性
隔离级别:读未提交、读已提交、可重复读、串行化
读未提交:脏读,一个事务可以读取另一个事务未提交的内容;
读已提交:不可重复读,不同的事务重复读了一条记录会出问题;
可重复读:一个事务在读取了记录之后,这条记录不能再被修改,也就是把这一行记录给锁住了,不允许其他的事物染指此行记录
串行化:一个事务必须等待另一个事务执行完才可以执行
Dubbo的配置分类
抽象类和接口类
抽象类 | 接口类 |
有方法的具体实现 | 只能有抽象方法 |
成员变量可以是任意类型的 | 成员变量只能是public static final类型的 |
可以有静态代码块和静态方法 | 不可以 |
一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
注意:JDK8之后,接口可以有默认方法和静态方法。
高并发
- 缓存
- 读写分离
- 数据库锁
Git
克隆 | git clone SSH/HTTPS路径 |
查看远程的所有分支列表 | git branch -a |
查看本地的git分支 | git branch |
切换分支 | git checkout 分支名 |
提交项目 | git add . |
git commit -m '说明' | |
git push origin 主支/分支名 | |
更新所有远程分支的本地分支 | git fetch |
合并分支 | git merge |
更新后自动合并到本地分支(相当于前面两个命令) | git pull |
放弃本地修改,使远程库内容强制覆盖本地代码(HEAD指向最新版本) | git reset --hard origin/master |
查看提交历史 | git log |
使用图形化工具来解决冲突 | git mergetool |
删除本地跟踪,避免合并冲突 | git rm -r --cached WEB-INF/classes |
git rm -r --cached .bpmn/* | |
git rm -r --cached .externalToolBuilders | |
git rm -r --cached .settings/* | |
git rm -r --cached .classpath | |
git rm -r --cached .mymetdata | |
git rm -r --cached target/* |
想学习更多,请进入https://www.liaoxuefeng.com/廖雪峰网站,进行学习