1.死锁
1)死锁的定义:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
2)java中查看死锁的工具:① Jstack命令 ②Jconsole工具
3)预防死锁的措施:①以确定的顺序获得资源 ②超时放弃
4)其他形式的死锁:①线程池死锁 ②网络连接池死锁
参考链接:https://juejin.im/post/5aaf6ee76fb9a028d3753534
2.sql索引及优化
参考链接1:https://juejin.im/post/5ab0f39df265da23866fba55(优化)
参考链接2:https://juejin.im/post/5a6873fbf265da3e393a97fa(索引)
3.http/2
简介:基于SPDY/2的基础上开发的新版本http协议
1)优点:比http/1.1及1的版本传输速度高出50%左右,文中举了一个例子,当同时请求379张图片时,HTTP/1.1加载用时4.54s,HTTP/2加载用时1.47s。
2)变化:①它在应用层和传输层之间加了一个二进制分帧,把来自传输层的数据再分成更小的消息和帧,并采用二进制编码。这种单连接多资源的方式,减小服务器的压力,使得服务器的内存变小,并且由于TCP请求条数的减少,减轻网络传输拥塞,加快慢启动,使得丢包和拥塞恢复得更快。
② 多路复用:多路复用允许同时通过单一的http/2 发送多重的“请求-响应”消息。
③ header压缩:http/1 每次都要携带大量且重复的header信息,http/2用了HPACK算法对header进行了压缩。
④ 服务器推送:简单来讲,就是当服务器和浏览器建立了连接之后,服务器会主动将一些资源推送给客户端并且缓存起来的机制。
参考链接:https://juejin.im/post/5ab6f8f06fb9a028ca52edc2
4.红黑树
1)定义:红黑树是一种自平衡的二叉查找树。
2)特点:节点颜色分为红色和黑色,根节点必为黑色,叶子节点也为黑色(null);一条路径中节点间必须红黑相间,不能够有超过两个的连续红或黑节点;从根节点沿不同路径到达同一个叶子节点经过的黑色节点个数相同;最长路径不超过最短路径的两倍。
3)调整方式:节点变色及旋转。
4)存在意义:弥补传统二叉查找树有可能插入不平衡的情况。
参考链接:https://juejin.im/post/5a27c6946fb9a04509096248
(这篇链接通过图画的方式,很简单易懂,可以对红黑树有一个最基本的了解,但是后面还是要自己多琢磨才能理解)
5.JVM的GC算法
1)垃圾判定算法:引用计数算法、可达性分析算法
①引用计数算法:通过判断对象的引用数量来决定是否回收。缺点是两个互相引用的对象无法回收,且很难处理循环引用。
②可达性分析算法(根搜索算法):以GC roots为起点,向下搜索,能够搜索到的引用对象即为可达节点,其余的视为不可达节点。根据树的可达性来判断对象是否被回收。如果对象不可达,则为可回收的。
PS:一般来说,对象要被判定为可回收要被标志两次及以上。
2)四种引用
①强引用:例如 Object c = new Object(),这样直接new出来的引用对象为强引用,一般不能回收强引用对象。
②软引用:指有用但非必需的引用对象。这种引用对象将会在发生内存溢出之前被纳入第二次回收,如果此次回收还未能获得足够空间,则会抛出内存溢出异常。
③弱引用:同样指非必需的引用对象。但它比软引用更弱,无论系统当前空间是否充足,只有弱引用关系的引用对象都会被回收。
④虚引用:最弱的一种引用。它存在的意义就是在所引用的对象被系统回收时收到通知。
3)JVM的垃圾回收算法:标记-清除算法、复制算法、标记-整理算法、分代搜集算法。
科普:老年代:指堆中很少垃圾回收的区域。
新生代:指堆中有大量垃圾回收的区域。
①标记-清除算法:定义:最原始的算法,标记可回收对象,然后清除。
缺点:效率不高,内存碎片多,不易进行空间分配。
②复制算法:定义:把内存空间分为Eden 和 Survivor 两部分,其中suvivor有两个。引用对象都放在Eden和其中一块suvivor中,等到垃圾回收时,把Eden中不可回收对象全部复制到其中的一个suvivor上,清理另外的suvivor和Eden。Eden一般空间分配较大,suvivor较小。
缺点:浪费空间,当不可回收对象多的时候,复制效率会变低;如果出现不可回收对象极端多的情况,并且还要借用老年代的空间。
③标记-整理算法:定义:在垃圾回收的时候,把不可回收对象往内存的一端移动,清理边界外的空间。适用于老年代的空间。
缺点:效率不高,不仅要标记不可回收对象,还要整理它们的引用地址,效率上不如复制算法。
④分代搜集算法:不能算新算法,它是融合了复制算法和标记整理-算法。就是在新生代中和老年代中使用各自合适的方法。新生代由于有大量新生成的对象,故回收对象多,采用复制算法合适;而老年代采用标记-清除或者标记-整理算法合适。
参考链接:http://www.cnblogs.com/wupeixuan/p/8670341.html
6.JAVA的深拷贝和浅拷贝
1)JAVA对象类型:基本类型和引用类型
浅拷贝:对于基本类型,即把当前对象的值拷贝;对于引用类型,即把当前对象的引用拷贝。
深拷贝:对于基本类型,即把当前对象的值拷贝;对于引用类型,即创建一个新对象,并按照当前对象的参数和配置来初始化。
2)clone()方法
对于一个成员变量只有基本类型的对象来说,对它实现clone()方法即为实现深拷贝;对于一个成员变量有引用变量类型的对象来说,对它实现clone()方法即为实现浅拷贝。因为对于类里面的引用变量,clone()方法不会创建一个新对象,而只是单纯地复制这个引用。
3)实现深拷贝的方法
① 序列化和反序列化
②自定义clone():在含有引用变量类型的对象中重写clone()方法,使得此引用变量在拷贝的时候也能创建一个新对象。
参考链接:https://juejin.im/post/5ab75dde5188255579189f2d
(这篇文章错别字有点多,而且部分地方好像打错了= =不过还是能看懂的)
7.java并发设计模式--不可变模式
1)不可变定义:即对象创建之后不能改变,更具体一点,即对象的属性都不变。对原对象的操作都将返回原对象的拷贝。
2)设计要点:①类的声明为final,防止父类对其修改。
②类的属性声明为private,并将所有setter()方法去掉,防止外界对其进行修改。
③类的属性声明为final,如果为可变对象类型,则要重新new一个对象返回。
3)注意:当类中的变量类型为可变对象的时候(如Date类型的变量),在类初始化的时候应该重新new一个新的对象返回。
4)优点和缺点:①优点:由于不可变,所以避免了线程同步的开销;避免了由于修改数据产生的异常。
②缺点:每次都要返回新对象,浪费空间,不易回收。
5)应用场景:①不适合数据大、创建频繁的对象,数据大且创建频繁的对象容易导致内存泄漏。
②适合于多线程场景中的同步,不适合于线程同步。
③适合表示抽象数据类型(例如数字、枚举、颜色等)的值
(其实应用场景我不太懂第二第三点)
参考链接:https://juejin.im/post/5ac1cbde518825558723b6e9
8.java中的值传递
1)概念:①形参:即形式参数,是在定义函数名和函数体中使用的参数,目的是用来接收调用该函数时传入的参数。
②实参:即实际参数,在调用有参函数时,主调函数和被调函数之间具有数据传输。实参即为被调函数函数名括号内的参数。
③值传递:传入的是一份实参的拷贝值,在函数内进行操作时不改变实参。
④引用传递:传入的是实参的地址,在函数内进行操作时改变实参。
⑤按共享传递:按共享传递,指的是在函数调用中,传递函数的是实参地址的拷贝。(如果实参在栈中,则直接拷贝实参。)
举个例子:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
pt.sout("Hollis");//实际参数为 Hollis
}
public void sout(String name) { //形式参数为 name
System.out.println(name);
}
2)值传递与引用传递的根本区别:实参是否有被拷贝,如果有,则为值传递;如果没有,则为引用传递。
3)java中为什么只有值传递:对于基本对象,拷贝的是值;对于引用对象,拷贝的是对象的引用。两者都是拷贝,而不是传递地址。
4)总结:无论是按值传递还是引用传递,都是一种求值策略。实际上,java中的传递应该属于按共享传递,按共享传递不过是值传递的一种特例。
参考链接:https://mp.weixin.qq.com/s/F7Niaa7nD1tLApCEGKAj4A
9.1多线程入门
之前一直没有用多线程,最近要写项目的客户通信部分,想着要用到,刚好重温一下(这部分我真的忘得一干二净)
1)概念:①进程:程序的一个执行过程
②线程:是进程的一部分,它与进程不同的是,多个线程共享一组资源和空间,也成为轻量级进程。
③多线程:几乎同时执行多个线程(几乎是因为,并不是同时执行,而是执行完一个再执行另外一个)
④进程和线程的区别:进程是相互独立的,而线程可能会互相影响。
2)使用方法:①继承Thread类 ②实现runnable接口
3)实例变量和线程安全
①实例变量:有共享和不共享之分。
②线程安全:在方法前实现加上synchronized关键字即可。
4)常用方法及停止多线程的方法
参考文章中的方法,停止多线程的方法为:return+interrupt;
5)线程的优先级别
级别有1-10,默认为5,数字越大则级别越高。优先级具有继承性。
6)多线程分类
①用户线程:运行在前台,执行具体的任务。
②守护线程:运行在后台,为前台线程服务。典型的守护线程有垃圾回收线程。可以通过调用Thead类的setDaemon(true)方法设置当前的线程为守护线程。
参考链接:https://juejin.im/post/5ab116875188255561411b8a
9.2 synchronized关键字
1)存在意义:为了解决线程安全问题,防止多个线程同时操作统一实例变量。
2)缺点:如果线程A获得了某对象的锁,且线程A执行任务时间长,则线程B必须等到A执行完才能继续执行。
2)产生的锁:synchronized产生的锁均为对象锁,即锁定一个对象。如果出现两个或多个线程同时访问同一个对象,则先获得锁的线程优先执行,其他线程等待;如果是访问多个对象,则会产生多个锁,线程之间互不干扰。(之前这里有点困惑) 如果多个线程同时访问同一对象未加synchronized关键字的方法,则会对该方法进行异步调用。只要加上synchronized关键字即可解决。此关键字可解决脏读问题。
3)synchronized锁可重入
①可重入锁:即自己可以再次获取自己的内部锁。如果锁不可重入,则会造成死锁。
②具有继承性:子类继承父类,可继承该特性。且出现异常时,会自动释放锁。
4)同步不具有继承性:如果一个父类带有synchronized关键字的方法,子类继承并重写该方法,该方法不能继承同步,要在该方法前加上synchronized关键字才可以。
参考链接:https://mp.weixin.qq.com/s/_xSYAxOqUrvZ7-L7dUxgGA
9.3 synchronized语句块
1)存在意义:解决synchronized关键字的缺点。
2)synchronized(this)同步代码块:当一个线程访问synchronized同步代码块时,此代码块为同步调用;另一个线程仍可访问该对象的非synchronized同步代码块,此代码块为异步调用。并且访问synchronized同步代码块的线程持有该对象的锁。
3)synchronized(object)代码块间使用:与2类似,如果object为同一个对象监视器,则线程执行同步,否则为异步。
4)静态同步synchronized方法和synchronized(class)代码块:给当前类上锁。对当前类的所有实例有效。
5)String具有的常量池属性:String具有常量池缓存的功能,所以尽量避免用synochronized(string)而是用synchronized(object),否则会出现两个线程持有相同的锁,导致只有一个线程能运行的结果。
参考链接:https://mp.weixin.qq.com/s/EIfN8XK-wz5FazxXfXEV3A
10.关于类的对象创建和初始化
1)初始化父类:当new一个子类对象时,父类对象不会被隐式创建。而是调用父类的构造函数和实例代码块,完成对继承而来的属性的初始化。
2)this和super关键字:this为当前实例对象的引用,super为当前实例对象的父类的引用。
3)完整的初始化过程
通过面试题来理解:
面试题一:
public class A {
static {
System.out.println("1");
}
public A(){
System.out.println("2");
}
}
public class B extends A {
static{
System.out.println("a");
}
public B(){
System.out.println("b");
}
}
Main 函数调用:
public static void main(String[] args){
A ab = new B();
ab = new B();
}
最终的输出结果为:1 a 2 b 2 b(这里为方便书写,把换行省略了)
分析:①类加载阶段:判断父类A,再判断子类B。发现都没有加载,在类加载的最后阶段,初始化阶段会调用编译器生成的方法,完成类中的所有静态变量赋值,包括静态块的执行。所以会打印出 1 a。
②实例初始化阶段:加载完后,开始实例化对象,执行new。实例化分为三个先后顺序阶段,实例属性字段的初始化,实例代码块的执行,构造函数的执行。先父类再子类,于是打印出2 b。
此时,第一条语句已经执行结束了。
然后是第二条语句,由于刚开始类已经加载完了,所以只要实例初始化,所以会打印出2 b。至此,两条语句执行结束。
面试题二:
public class X {
Y y = new Y();
public X(){
System.out.println("X");
}
}
public class Y {
public Y(){
System.out.println("Y");
}
}
public class Z extends X {
Y y = new Y();
public Z(){
System.out.println("Z");
}
}
Main 函数调用:
public static void main(String[] args){
new Z();
}
输出的结果为:Y X Y Z。运用以上分析可得到。
参考链接:https://juejin.im/post/5acc72bdf265da23766b9da7
11.关于redis(内容有点点点点多)
参考链接:https://juejin.im/post/5ad6e4066fb9a028d82c4b66
12.JAVA后端开发规范
看了这篇文章之后,收获很多,无论是在编码规范还是sql相关,我发现很多我都没注意到。
参考链接:https://juejin.im/post/5ada99fff265da0b8a672fbd