多线程面试问题,背完去面试 (面试篇二)

  最近把多线程又看了一遍,已经是第三次系统的看了,发现多线程跟JVM联系还是很大的。刚好就结合在一起总结了一下。声明这篇博客是总结性的,你有一定的基础,帮你捋一遍多线程的重要知识点。


1. 我感觉多线程跟JVM本身还是有很多相似之处的。我就将两者结合起来看了。进程由多个线程组成,每个线程又可以在虚拟机栈中开辟多个栈帧空间。买个栈帧由局部变量,操作数指针表,动态链接堆对象的地址,返回地址,附加信息组成。保证多线程安全就是保证数据的原子性,可见性和有序性。常见的总线加锁(降低CPU的吞吐量),或者实现MESI(缓存上的一致性协议)。

2. 多线程下每个线程处理一个变量,该变量存放在工作内存区域,会将变量读取到私有空间,在私有空间处理完成之后,再返给公有空间,所以多线程下数据不能保证正确性。

3. 首先是Synchronized关键字,可以用来修改代码块和方法。它的原理是锁住你需要的对象(类由多个对象组成),一次只允许一个线程对其进行操作,重量级的锁。利用jclasslib查看,synchronized锁方法块的时候,使用了monitorenter 和 monitorexit,针对每个对象都有个锁计数器,来一个加一个,释放一个减一个,用的时候容器产生死锁。当锁住方法的时候,就有access flags的标志位。类由三部分组成对象头区域,实例数据区域,对齐填充区域。对象头区域又包括Markdown专门用来记录锁的标志位占8字节,其中32位用来记录锁的相关信息。类型指针(指针压缩部分),数组长度。

4. 因为Synchronized是重量级的锁,所以在Markdown部分改进了锁,有了偏向锁,设置计算器。然后轻量级的锁,自旋锁。锁消除(JIT即时编译)。锁可以升级,但是不会降级。Synchronized是利用互斥性来保证的线程安全的。保证原子性和可见性。

5. volatile保证了多线程的可见性和有序性。Volatile不能保证原子性。多个线程操作数据的时候,还是会出问题。汇编层反编译可以看出volatile对变量使用了lock变量。Volatile底层模拟了MESI协议,当线程写操作的操作,将变量的CACHE_LINE置为无效,别的线程只能从主内存中获取。Volatile修饰的上下文变量无法指令重排序(JS的预编译)。轻量级锁,但是无法保证原子性。相比于Synchronized还不会阻塞。

6.对于以上的缺点,改进的方法CAS和AQS理论。CAS(CompareAndSwap)(V,E,N)比较交换,底层就一句话的,调用了unsafe的vm方法,直接是底层的C++方法cxmnchg。如果CPU指令过快,AtomicStampedReference排序。乐观锁和悲观锁,乐观锁,mysql的version字段。

7.CAS不适用于大量线程的时候,因此AQS(AbstractQueuedSynchronized 同步发生器)。自己extends AQS,然后implements Lock接口,就是一个简单的ReentrantLock()。LOCK内存是CLH同步双端队列,为了减少线程间切换的开销,引入自旋锁。会等待5~10次,公平,非公平抢占资源,FIFO队列。ReentranLock又改进了读写分离锁。

8. 自己也可以管理线程池,但是麻烦,因为涉及GC,Executors改进了finalize()方法,利用SecurityManager()进行管理,线程池只能释放,不能被回收。ThreadPoolExecutor()里面的参数都是比较重要的,定义了线程池的基本信息,尤其有一个Blocking 阻塞队列的方法,无界队列给cached用的。

线程的生命周期:Running Shutdown Stop tidying isterminated

线程池的生命周期:开始(Executors有了)。

9.几个工具类  

CountDownLatch 倒计时。CyclicBarrier 一起跑步。

Semaphore 类似FixedThreadPools

集合类 ConcurrentLinkedDeque(非阻塞)  LinkedBlockingDeque(阻塞)。

10. 死锁产生的四个必要条件

  互斥  请求与保持  非剥夺  循环等待


很多不足之处,希望大家指出。

你可能感兴趣的:(Java工具类)