今天读了《如何准备阿里社招面试,顺谈Java程序员学习中各阶段的建议》,一直对作者是一个仰视的姿势,崇拜已久,为了便于自己学习和总结,在这里对文中出现的几个技术问题进行了学习,在这进行如下总结:
1、HashMap 有顺序吗?
答:Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。
2、有没有有顺序的Map实现类?他们是如何保证他们的顺序的?它们两个哪个的有序实现比较好?
答:TreeMap、LinkedHashMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。
TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。
3、如果想实现所有的线程一起等待某个事件的发生,当某个事件发生时,所有线程一起开始往下执行的话,有什么好的办法吗?
答:栅栏
-
栅栏类似闭锁,但是它们是有区别的.
闭锁用来等待事件,而栅栏用于等待其他线程.什么意思呢?就是说闭锁用来等待的事件就是countDown事件,只有该countDown事件执行后所有之前在等待的线程才有可能继续执行;而栅栏没有类似countDown事件控制线程的执行,只有线程的await方法能控制等待的线程执行. -
CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。
- 原文链接:http://blog.csdn.net/csujiangyu/article/details/44338307
原文链接:http://blog.csdn.net/yczz/article/details/38761237
5、NIO中selector的功能及实现原理?
功能:Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
原理:原文链接:http://www.cnblogs.com/huaf/archive/2011/02/12/2373002.html
Selector是用来获取注册在其中的channel的相关事件的发生,也就是accept,read和write。selector中有3个key set。
key set:包含代表所有在其中注册的channel,可以通过selector.keys()得到。
selected-key set:包含所有被检测到有关注的操作已经就绪的key,通过selector.selectedKeys得到。
cancelled-key set:包含所有已经被cancel,但是还没有channel还没有deregister的key,这个集合是不能直接被访问的。
key通过调用channel的register方法被加入到key set中。被取消的key在select的时候会被从相应的key set中移除。
key set自身是不可以直接进行修改的。 无论是通过调用channel的close方法还是调用key的cancel方法,key都会被放置到canceled-key set中。取消的key会将其channel在下一次select时将注册撤销,同时将key从所有的key set中移除。
key在执行select操作时被加入到selected-key set中。在selected-key set中的key可以通过调用iterator的remove方法,将其从selected-key set中移除,不能通过其他的办法将其从selected-key set中移除。
通过selector的3个方法select(阻塞选择,直到有关心的事件发生时退出阻塞),selectNow(不阻塞选择),select(long)(指定超时选择,超时到达或者有关心事件发生时退出阻塞),来获取关心事件的发生。其执行步骤分为以下3步:
1、将存在于canceled-key set中的key从所有的key set中移除,撤销注册的channel,清空canceled-key set。
2、地层操作系统检查是否有关心的事件发生,当有关心的事件发生时,首先检查channel的key是否已经存在于selected-key set中,如果不存在,则将其加入到selected-key set中去,同时修改key的ready-operation set来表明当前ready的操作,而以前存在于ready-operation set中的信息会被删除。如果对应的key已经存在于selected-key set中,这直接修改其ready-operation set来表明当前ready的操作,删除原来ready-operation set中的信息。
3、如果在第二步中有加入到canceled-key set中的key,在这一步会执行第一步的操作。
selector自身是线程安全的,而他的key set却不是。在一次选择发生的过程中,对于key的关心事件的修改要等到下一次select的时候才会生效。另外,key和其代表的channel有可能在任何时候被cancel和close。因此存在于key set中的key并不代表其key是有效的,也不代表其channel是open的。如果key有可能被其他的线程取消或关闭channel,程序必须小心的同步检查这些条件。
6、什么时候一个对象会被GC?
答:gc判读一个对象是否要被回收,主要是依据这个对象是否可达。
不能仅仅根据引用数量来确定一个对象是否可以被回收,比如:对象A被对象B引用,但对象B本身已经处于不可达状况,这样对象A和对象B就形成一个孤岛,下一次回收的时候A和B都会被回收。
7、GC都有哪些分类?这些策略分别都有什么优劣势?都适用于什么场景?
- 串行垃圾回收器(Serial Garbage Collector)
- 并行垃圾回收器(Parallel Garbage Collector)
- 并发标记扫描垃圾回收器(CMS Garbage Collector)
- G1垃圾回收器(G1 Garbage Collector)
8、Java的类加载器都有哪些?每个类加载器都加载哪些类?这些类加载之间的父子关系是怎样的?
主要分为Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader和User Defined ClassLoader。
启动类加载器(Bootstrap ClassLoader):
这个类加载器使用C++语言实现,并非ClassLoader的子类。主要负责加载存放在JAVA_HOME / jre / lib / rt.jar里面所有的class文件,或者被-Xbootclasspath参数所指定路径中以rt.jar命名的文件。
扩展类加载器(Extension ClassLoader):
这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载AVA_HOME / lib / ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。
应用程序类加载器(Application ClassLoader):
这个加载器由sun.misc.Launcher$AppClassLoader实现,它负责加载classpath对应的jar及目录。一般情况下这个就是程序中默认的类加载器。
自定义类加载器(User Defined ClassLoader):
开发人员继承ClassLoader抽象类自行实现的类加载器,基于自行开发的ClassLoader可用于并非加载classpath中(例如从网络上下载的jar或二进制字节码)、还可以在加载class文件之前做些小动作 如:加密等。
原文链接:http://www.importnew.com/22270.html9、什么是双亲委派模型?为什么Java的类加载器要使用双亲委派模型?
上图中所展示的类加载器之间的这种层次关系,就称为类加载器的双亲委托模型。双亲委托模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public abstract class ClassLoader {
private static native void registerNatives();
static {
registerNatives();
}
// The parent class loader for delegation
private ClassLoader parent;
// Hashtable that maps packages to certs
private Hashtable package2certs = new Hashtable( 11 );
}
|
双亲委托的工作过程:如果一个类加载器收到了一个类加载请求,它首先不会自己去加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成加载请求(它管理的范围之中没有这个类)时,子加载器才会尝试着自己去加载。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,无论那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,相反,如果没有双亲委托模型,由各个类加载器去完成的话,如果用户自己写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不同的Object类,java类型体系中最基本安全行为也就无法保证。