时间过得挺快的,十月份还剩一周了,回顾过去,展望未来,技术旅程,重在积累实践总结上。
大家都知道进入一家不错的企业都要经历一个很重要的环节那就是面试,面试是检验候选人是否符合岗位要求,技术面试一般都是有浅往深问,这个时候最考验候选人的技术能力了,思路大概是:
首先考察候选人的基础知识掌握情况;再通过深度考察是否有技术热情和深度以及技术的广度;提出一些质疑和挑战来考察候选人能否与有不同意见,这个最好能回答不同好的意见具有加分项;要想面试成功拿到offer,面试准备这个环节必不可少的,有一句俗语说的很对“好记性不如烂笔头”,对于自己需要掌握的技术知识点要经常积累。 遇到比较难的问题不要逃避问题,要深入思考去解决,这样解决一个个难题积累下来整理成文档,自然而然的就有了自己的个人体系,面试时候看一下就知道很多知识点。 如何将自己的技术水平很好的展现出来呢? 1.面试前看下面试单位的JD介绍,针对考察内容能否很快的在自己的脑海想到最优解答思路,如果没有想到就应该把问题列出来,找资料,整理最优解答案以及扩展知识点。 2.整理自己简历,要把你简历里的项目中最大的技术难点和亮点突出出来,并且有很好的解决方案,整理好思路,不至于面试过程中出现卡壳的现象。 3.在跟面试官进行技术沟通过程中要做到有理有据,不卑不亢, 4.在技术问题上坚持自己的客观和原则,根据共同认可的事实进行逻辑判断得出观点。
三者都可以可以储存和操作字符串。其中 String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。而 StringBuffer和StringBuilder 类表示的字符串对象可以直接进行修改。StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized 修饰,因此它的效率也比 StringBuffer 要高。
如果操作少量的数据用String单线程下操作大量的数据用StringBuilder多线程下操作大量的数据用StringBufferMap接口和Collection接口是所有集合框架的父接口: Collection接口的子接口包括:Set接口和List接口 Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等 Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等 List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间 LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。 HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 TreeMap: 红黑树(自平衡的排序二叉树)
2.实现 Runnable 接口。 如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口。
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数 据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新), 如果失败则要重复读-比较-写的操作。 java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。
悲观锁 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。 java 中的悲观锁就是 Synchronized,AQS 框架下的锁则是先尝试 cas 乐观锁去获取锁,获取不到, 才会转换为悲观锁,如 RetreenLock。
自旋锁 如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋), 等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。 线程自旋是需要消耗 cup 的,说白了就是让 cup 在做无用功,如果一直获取不到锁,那线程 也不能一直占用 cup 自旋做无用功,所以需要设定一个自旋等待的最大时间。 如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其它争用锁 的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。
Synchronized 同步锁 synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。 Synchronized 作用范围
作用于方法时,锁住的是对象的实例(this);当作用于静态方法时,锁住的是 Class 实例,又因为 Class 的相关数据存储在永久带PermGen (jdk1.8 则是 metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁, 会锁所有调用该方法的线程;synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列, 当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。 Synchronized 核心组件: Wait Set:哪些调用 wait 方法被阻塞的线程被放置在这里;Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中;Entry List:Contention List 中那些有资格成为候选资源的线程被移动到 Entry List 中;OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被成为 OnDeck;Owner:当前已经获取到所资源的线程被称为 Owner;!Owner:当前释放锁的线程。ReentrantLock锁 ReentantLock 继承接口 Lock 并实现了接口中定义的方法,他是一种可重入锁,除了能完成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等 避免多线程死锁的方法。ReentrantLock 在构造函数中提供了 是否公平锁的初始化方式,默认为非公平锁。
两者的共同点:
都是用来协调多线程对共享对象、变量的访问都是可重入锁,同一线程可以多次获得同一个锁都保证了可见性和互斥性 两者的不同点: ReentrantLock 显示的获得、释放锁,synchronized 隐式获得释放锁ReentrantLock 可响应中断、可轮回,synchronized 是不可以响应中断的,为处理锁的 不可用性提供了更高的灵活性ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的ReentrantLock 可以实现公平锁ReentrantLock 通过 Condition 可以绑定多个条件底层实现不一样, synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻塞,采用的是乐观并发策略Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现。synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象, 因此使用 Lock 时需要在 finally 块中释放锁。Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时, 等待的线程会一直等待下去,不能够响应中断。通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。Lock 可以提高多个线程进行读操作的效率,既就是实现读写锁等。CAS(Compare And Swap/Set)比较并交换,CAS 算法的过程是这样:它包含 3 个参数CAS(V,E,N)。V 表示要更新的变量(内存值),E 表示预期值(旧的),N 表示新值。当且仅当 V 值等于 E 值时,才会将 V 的值设为 N,如果 V 值和 E 值不同,则说明已经有其他线程做了更新,则当 前线程什么都不做。最后,CAS 返回当前 V 的真实值。 CAS 操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时 使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂 起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理, CAS 操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
CAS 会导致“ABA 问题”?CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。 比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操 作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是不代表这个过 程就是没有问题的。 部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修 改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本 号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问 题,因为版本号只会增加不会减少。
注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一 般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中 也可以配置非 Singleton。
Destroy 过期自动清理阶段 当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用那个其实现的 destroy()方法; destroy-method 自配置清理 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口, 则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
JDK 动态代理 主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类 的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。CGLib 动态代理 :CGLib 全称为 Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新 的 class。和 JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。