面试题(五)

ThreadLocal(线程变量副本)

    Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.
    采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立的改变自己的副本,而不会和其他线程的副本冲突.
    ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本.
    ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean,事务管理,任务调度,AOP等模块都出现了它的身影.
    Spring中绝大部分都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以Singleton方式在多线程中正常工作了.

java内存模型

    java虚拟机规范中将java运行时数据分为六种.
    1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址,java虚拟机的多线程就是通过线程轮流切换并分配时间来实现,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为”线程私有”.
    2.java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值,局部
变量表放着基本数据类型,还有对象的引用.
    3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务.
    4.java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存.
    5.方法区:各个线程共享的区域,存储虚拟机加载的类信息,常量,静态变量,编译后的代码.
    6.运行时常量池:代表运行时每个class文件中的常量表,包括几种常量:编译时的数字常量,方法或者域的引用.

java GC是在什么时候,对什么东西,做了什么事情?

在什么时候:

    1.在新生代有一个Eden区和两个survivor区,首先将对象放入Eden区,如果空间不足就向其中一个survivor区上放,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放入另一个survivor区中,然后清空Eden和之前的那个survivor区的内存里去.
    2.大对象以及长期存活的对象直接进入老年区.
    3.当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GCyiy以尽可能的获得老年区的空间.

对什么东西:

    从GC Roots搜索不到,而且经过一次标记清理之后仍没有复活的对象.

做什么:

    新生代:复制清理;
    老年代:标记-清理和标记-压缩算法;
    永久代:存放java中的类和加在类的类加载器本身.

GC Roots都有哪些:

    1.虚拟机栈中的引用的对象
    2.方法区中静态属性引用的对象,常量引用的对象
    3.本地方法栈中JNI(即一般说的Native方法)引用的对象.

Synchronized与Lock

    Synchrnized与Lock都是可重入锁,同一个线程再次进入同步代码的时候,可以使用自己已经获取到的锁.
    Synchronized是悲观锁机制,独占锁.而Locks.ReentrantLock是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止.
ReentrantLock适用场景
    1.每个线程在等待一个锁的控制权的这段时间需要中断.
    2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程,锁可以绑定多个条件.
    3.具有公平锁功能,每个到来的线程将排队等待.

String,StringBuffer与StringBuilder之间区别

    StringBuffer是线程安全的,每次操作字符串,String会生成一个新的对象,而StringBuffer不会,StringBUilder是非线程安全的.
    三者在执行速度方面比较:StringBuilder>StringBuffer>String
三者的总结
    1.如果操作少量的数据用String
    2.单线程操作字符串下操作大量数据用StringBuilder
    3.多线程操作字符串缓冲流下操作大量数据用StringBuffer

fail-fast

    机制是java集合(Collection)中的一种错误机制,当多个线程对同一个集合的内容操作时,就会产生fail-fast事件
    例如:当某个线程A通过iterator去遍历集合的过程中,当该集合的内容被其他线程所改变,那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件

happens-before

    如果两个操作之间具有happens-before关系,那么钱一个操作的结果就会对后面一个操作可见.
    1.程序顺序规则:一个线程中的每一个操作,happens-before于该线程中的任意后续操作.
    2.监视器锁规则:对一个监视器锁的解锁,happens-before于随后对这个监视器锁的加锁.
    3:volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读.
    4.传递性:如果A happens-before B,且B happens-before C,那么A happens-before C.
    5.线程启动规则:Thread对象的start()方法happens-before于此线程的每一个动作.

Volatile和Synchronized四个不同点:

1.粒度不同,前者锁对象和类,后者针对变量
2.synchronized阻塞,volatile线程不阻塞
3.synchronized保证三大特性,volatile不保证原子性
4.synchronized编译器优化,volatile不优化
多线程三大特性
1.原子性:事务有原子性,即一个操作或多个操作要么执行的过中不被任何元素打断,要么不执行.
2.可见性:当多个线程共享同一个变量时,其中一个线程修改了这个变量,其他线程能够立即看到修改后的值.
3.有序性:线程按顺序执行,线程中有代码,即让代码按顺序执行,优化代码.
volatile具备两种特性:
1.保证此变量对所有线程的可见性,指一条线程修改了这个变量的值,新增对于其他线程来说是可见的,但并不是多线程安全的.
2.禁止指令重排序优化.
volatile如何保证内存可见性:
    1.当写一个volatile变量时,JVM会把该线程对应的本地
内存中的共享变量刷新到主内存中.
    2.当读一个volatile变量时,JVM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量.

同步 异步 阻塞 非阻塞

同步:就是一个任务的完成需要依赖另外一个任务,只有等待被依赖的任务完成之后,依赖任务才能完成.
异步:不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,只要自己任务我那成了就算完成,被依赖的任务是否弯沉会通知回来.(异步的特点就是通知)
阻塞:CPU停下来等一个慢的操作完成之后,才会接着完成其他的工作.
非阻塞:就是在这个慢的操作执行时,CPU去做其他工作,等这个慢的操作完成后,CPU才会接着完成后续的操作.

猜你喜欢

转载自blog.csdn.net/kbh528202/article/details/80502487