面试官:怎么样才算是线程安全?
你:在多线程环境下,被多个线程同时操作的共享的,可修改的状态的正确性。
面试官:如何保证线程安全?
你:用锁synchronized 和 ReentranctLock包裹起来,或者状态自身不可变。
面试官:介绍下synchronized 和 ReentranctLock?
你:synchronized 和 ReentranctLock都是同步互斥锁。
写法上不同:
synchronized比较简单,ReentrantLock必须配合try-catch-finally来使用lock,unlock。还提供了很多API,如tryLock,lockInterruptlly。
控制粒度不同:
synchronized控制一个代码块,ReentrantLock可以控制代码块,同时还可以配合Condition实现多个方法的互选交互,例如ArrayBlockingQueue中take,put,利用notEmpty和notFull来控制等待。语法上代替了抽象的wait,notify,notifyAll,主动的调用。
ReentrantLock可以控制公平性fairness,synchronized不可以。
实现原理上的不同:
synchronized是jvm内置的锁,ReentrantLock是基于AbstractQueuedSynchronizer(AQS)。AQS中使用CAS来设置state,判断是否可重入。AOS(AbstractOwnableSynchronizer AQS的父类)存放占有锁的线程。
面试官:说说synchronzied 原理?
你:synchronized是jvm的内部锁,基于monitorenter monitorexit指令实现。
面试官:jvm在java 6之后加入了锁的升降机制,说说从未锁定,偏斜锁,轻量锁,重量锁的过程。
你:没有多个不同线程并发的情况下,jvm会优先使用偏斜锁。通过CAS修改对象头的mark word,在hashcode块上存放threadId和epoch时间点,以及是否可偏斜锁的标记0, 1,锁标记位00, 10, 11, 01。
有多个不同线程并发,jvm会自动升级到轻量锁或者重量锁。
在safepoint,也就是STW的时候,会对空闲的monitor进行降级。
面试官:读多写少的情况下,你会使用什么锁?
你:可以使用ReentranctReadWrtieLock,StampedLock
面试官:等待所有线程获得许可,才开始
你:Semaphore
面试官:等待所有线程结束,才结束
你:CountDownLatch
面试官: Thread start()两次为出现什么异常
你:会出现IllegalStateException,在start方法里会去判断threadStatus,0表示NEW。start()以后由jvm来控制thread state。
面试官:Thread State有哪些?
你:NEW 还没开始not start yet
RUNNABLE 就绪,可执行或者在等待CPU资源
BLOCKED 阻塞,等待锁
WAIT 等待其他线程完成并notify
TIMED_WAIT 超时等待
TERMINATED 线程死了
面试官:线程创建方法有哪些?
你:implements Runnable, extends Thread
面试官:开发中使用Runnable,Thread那个比较好?
你:使用Runnable比较好,因为可以配合Thread或者Executor来使用,可重复使用。
面试官:ThreadLocal介绍下?
你:ThreadLocal是每个线程私有的,保存线程私有信息的机制。ThreadLocal不要和线程池配合使用,因为ThreadLocal只有在线程结束时才会被回收,而线程池的worker线程只有在线程池退出时才结束。这样可能会导致OOM。
面试官:多线程中判断状态时,用while还是if?
你:用while比if好,可以避免spurious wakeup,也就是状态明明改了,但是没读到。
面试官:什么是死锁?
你:死锁就是线程之间互相持有对方需要的锁,互相等待,造成的,另外还有资源不释放,造成永久等待。
面试官:如何解决死锁?
你:jps ps找PID,jstack PID获取线程栈找到BLOCKED状态的线程,按照waiting的锁的ID就可以找到造成死锁的线程。
面试官:如何防止死锁?
你:利用timed_wait方法来防止。
尽量少使用多个锁嵌套的方式。如果需要多个锁嵌套,要将锁按时间顺序,列出可能的获取锁的顺序,看看有没有在同一时间的交集。
利用工具排查,例如FindBugs。