前段时间去了一家互联网公司面试,感受被虐的挺惨的.主要缘由仍是本身对jdk不是很是熟悉,面试官没有让本身去写代码实现,也没有考数据结构和算法,只是让说而已.我以为程序员不是靠说来证实本身,就像那句名言:talk is cheap,show me the code.还有就是和面试官不是很谈得来,就是没有缘分.好了,如今总结一下面试的问题,而后写下关于这些问题的答案.
java
问题:程序员
1.画出项目流程图面试
我画了,可是面试官说画的比较简单,要求像程序的流程图同样,输入时什么,输出是什么.其实我在公司作的项目是财务部分, 公司 主要追寻敏捷开发,文档太少了,几乎等于没有,咱们都是先问下同事关于业务部分,而后直接看代码.并且这个部分的业务很是复杂,没那么简单就能画出,画出来也须要很是多的时间.
算法
2.volatile关键字做用
数组
在Jvm内存中有一块是虚拟机栈,每一个线程运行的时候都会有本身的栈,线程栈中保存了一份线程变量的备份,是从主存中复制来的,这样在取出来以后的操做中没法保证主存中的数据有没有在线程操做的过程当中发生了改变.若是对变量加了volatile修饰,变量每次在使用的时候会从主存中去取,可是这仍是会产生并发的问题.因此仍是建议使用sychonized比较安全
安全
3.HashMap在多线程的状况下会不会有问题.
数据结构
最容易让人想到的就是脏数据,其次就是循环的问题.多线程
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; // 标记1 addEntry(hash, key, value, i); return null; }
void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); }
void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); //标记2 size++; }
在标记1这个地方假设两个线程都PUT同一个KEY ,A线程到了这里,而后切到B线程,B线程也到了标记1这里,而后A进入到addEntry方法中这时候, table数组相应的位置放的就是put进来的Key Value的Entry,一样B线程进入到addEntry就会也生成一个相同key和value的Entry 他的next指向以前A线程建立的Entry,这里就产生了脏数据.
并发
3.Hashtable 和CocurrentHashMap区别
jvm
public synchronized V put(K key, V value){ ... } public synchronized V get(Object key){ ... }
可见Hashtable几乎对全部的方法上面读加了synchronized关键字,等同于锁住了整个Map表,同一时刻只能有一个线程操做同一个Hashtable对象中被sychonized修饰的方法,效率大大下降.ConcurrentHashMap东西太多了.好多地方看不懂,大概就是分了多个Segment每一个Segment里面装的是Entry数组就等于Hashtable,这样操做的粒度就小了好多.在读的时候几乎能够实现全并发,写的时候只是锁对应的Segment,而非整个Segment数组
4.Thread.sleep(),Object.await(),Object.notify(),Thread.yeild(),Object.join()的区别,
Thread.sleep(100)是当前线程不让出系统资源的状况下去睡觉,并且不会释放对象锁.Object.wait()是在同步控制块或者方法中使用的,表示被锁的对象进行释放,而,notify是随机唤醒以前在这个对象上进行wait()的线程.而notifyAll是唤醒全部在这个对象wait()的线程,而后由线程去争抢对象锁,由于这些线程都在sychronized中.yeild()是在不释放对象锁的状况下进入就绪队列与相同及以上优先级的线程从新竞争cpu,有可能下次仍是调用yeild的线程获取到cpu.Thread.join()开启一个子线程,可是必需要等待子线程执行完成以后再执行Thread.join()以后的代码.
5.Jvm在何时回收,回收算法,jvm内存.
Jvm内存区域主要分红,堆,虚拟机栈,本地方法栈,方法区,程序计数器.
程序计数器:是线程私有的,指向的是当前线程执行的字节码指令的地址.若是是本地方法则为空.
虚拟机栈:线程进入某个方法的时候建立栈帧,用于存放局部变量等信息. 本地方法栈用于本地方法
方法区:也被成为永代区,这是全部线程共享的区域,存放了类的基本信息,静态变量等,里面还有的变量池,变量池主要存放的是运行前就肯定的对象,能在程序运行时进行扩展.
回收算法主要包括,标记清除,标记整理,复制,分代. 通常状况下使用分代分红新生代和老年代,新生代使用复制算法,老年代是用标记整理算法.