ThreadLocal(线程变量副本)css
Synchronized实现内存共享,ThreadLocal为每一个线程维护一个本地变量。html
采用空间换时间,它用于线程间的数据隔离,为每个使用该变量的线程提供一个副本,每一个线程均可以独立地改变本身的副本,而不会和其余线程的副本冲突。java
ThreadLocal类中维护一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。web
ThreadLocal在Spring中发挥着巨大的做用,在管理Request做用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。算法
Spring中绝大部分Bean均可以声明成Singleton做用域,采用ThreadLocal进行封装,所以有状态的Bean就可以以singleton的方式在多线程中正常工做了。sql
Java内存模型:数据库
Java虚拟机规范中将Java运行时数据分为六种。vim
1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是经过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都须要一个独立的程序计数器,互不影响,该区域为“线程私有”。跨域
2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操做栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。数组
3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
4.Java堆:全部线程共享的一块内存区域,对象实例几乎都在这分配内存。
5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
6.运行时常量池:表明运行时每一个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。
“你能不能谈谈,java GC是在何时,对什么东西,作了什么事情?”
在何时:
1.新生代有一个Eden区和两个survivor区,首先将对象放入Eden区,若是空间不足就向其中的一个survivor区上放,若是仍然放不下就会引起一次发生在新生代的minor GC,将存活的对象放入另外一个survivor区中,而后清空Eden和以前的那个survivor区的内存。在某次GC过程当中,若是发现仍然又放不下的对象,就将这些对象放入老年代内存里去。
2.大对象以及长期存活的对象直接进入老年区。
3.当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,若是这些立刻要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GC以尽量地得到老年区的空间。
对什么东西:从GC Roots搜索不到,并且通过一次标记清理以后仍没有复活的对象。
作什么:
新生代:复制清理;
老年代:标记-清除和标记-压缩算法;
永久代:存放Java中的类和加载类的类加载器自己。
GC Roots都有哪些:
1.虚拟机栈中的引用的对象
2.方法区中静态属性引用的对象,常量引用的对象
3.本地方法栈中JNI(即通常说的Native方法)引用的对象。
Synchronized 与Lock都是可重入锁,同一个线程再次进入同步代码的时候.可使用本身已经获取到的锁。
Synchronized是悲观锁机制,独占锁。而Locks.ReentrantLock是,每次不加锁而是假设没有冲突而去完成某项操做,若是由于冲突失败就重试,直到成功为止。
ReentrantLock适用场景
1.某个线程在等待一个锁的控制权的这段时间须要中断。
2.须要分开处理一些wait-notify,ReentrantLock里面的Condition应用,可以控制notify哪一个线程,锁能够绑定多个条件。
3.具备公平锁功能,每一个到来的线程都将排队等候。
StringBuffer是线程安全的,每次操做字符串,String会生成一个新的对象,而StringBuffer不会;StringBuilder是非线程安全的。
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 syn阻塞,volatile线程不阻塞
3 syn保证三大特性,volatile不保证原子性
4 syn编译器优化,volatile不优化
volatile具有两种特性:
1.保证此变量对全部线程的可见性,指一条线程修改了这个变量的值,新值对于其余线程来讲是可见的,但并非多线程安全的。
2.禁止指令重排序优化。
Volatile如何保证内存可见性:
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
2.当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
同步:就是一个任务的完成须要依赖另一个任务,只有等待被依赖的任务完成后,依赖任务才能完成。
异步:不须要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工做,只要本身任务完成了就算完成了,被依赖的任务是否完成会通知回来。(异步的特色就是通知)。
打电话和发短信来比喻同步和异步操做。
阻塞:CPU停下来等一个慢的操做完成之后,才会接着完成其余的工做。
非阻塞:非阻塞就是在这个慢的执行时,CPU去作其余工做,等这个慢的完成后,CPU才会接着完成后续的操做。
非阻塞会形成线程切换增长,增长CPU的使用时间能不能补偿系统的切换成本须要考虑。
CAS(Compare And Swap) 无锁算法:
CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。
线程池的做用:
在程序启动的时候就建立若干线程来响应处理,它们被称为线程池,里面的线程叫工做线程。
第一:下降资源消耗。经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
第二:提升响应速度。当任务到达时,任务能够不须要等到线程建立就能当即执行。
第三:提升线程的可管理性。
经常使用线程池:ExecutorService 是主要的实现类,其中经常使用的有
Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。
类加载器工做机制:
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.链接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用。
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工做。
双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成。
用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
一致性哈希:
Memcahed缓存:
数据结构:key,value对
使用方法:get,put等方法
Redis数据结构: String—字符串(key-value 类型)
Hash—字典(hashmap) Redis的哈希结构可使你像在数据库中更新一个属性同样只修改某一项属性值。
List—列表 实现消息队列
Set—集合 利用惟一性
Sorted Set—有序集合 能够进行排序
能够实现数据持久化
索引:B+,B-,全文索引
Mysql的索引是一个数据结构,旨在使数据库高效的查找数据。
经常使用的数据结构是B+Tree,每一个叶子节点不但存放了索引键的相关信息还增长了指向相邻叶子节点的指针,这样就造成了带有顺序访问指针的B+Tree,作这个优化的目的是提升不一样区间访问的性能。
何时使用索引:
1.常常出如今group by,order by和distinc关键字后面的字段
2.常常与其余表进行链接的表,在链接字段上应该创建索引
3.常常出如今Where子句中的字段
4常常出现用做查询选择的字段
Spring IOC (控制反转,依赖注入)
Spring支持三种依赖注入方式,分别是属性(Setter方法)注入,构造注入和接口注入。
在Spring中,那些组成应用的主体及由Spring IOC容器所管理的对象被称之为Bean。
Spring的IOC容器经过反射的机制实例化Bean并创建Bean之间的依赖关系。
简单地讲,Bean就是由Spring IOC容器初始化、装配及被管理的对象。
获取Bean对象的过程,首先经过Resource加载配置文件并启动IOC容器,而后经过getBean方法获取bean对象,就能够调用他的方法。
Spring Bean的做用域:
Singleton:Spring IOC容器中只有一个共享的Bean实例,通常都是Singleton做用域。
Prototype:每个请求,会产生一个新的Bean实例。
Request:每一次http请求会产生一个新的Bean实例。
代理的共有优势:业务类只须要关注业务逻辑自己,保证了业务类的重用性。
Java静态代理:
代理对象和目标对象实现了相同的接口,目标对象做为代理对象的一个属性,具体接口实现中,代理对象能够在调用目标对象相应方法先后加上其余业务处理逻辑。
缺点:一个代理类只能代理一个业务类。若是业务类增长方法时,相应的代理类也要增长方法。
Java动态代理:
Java动态代理是写一个类实现InvocationHandler接口,重写Invoke方法,在Invoke方法能够进行加强处理的逻辑的编写,这个公共代理类在运行的时候才能明确本身要代理的对象,同时能够实现该被代理类的方法的实现,而后在实现类方法的时候能够进行加强处理。
实际上:代理对象的方法 = 加强处理 + 被代理对象的方法
JDK和CGLIB生成动态代理类的区别:
JDK动态代理只能针对实现了接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现了相同的接口,目标对象做为代理对象的一个属性,具体接口实现中,能够在调用目标对象相应方法先后加上其余业务处理逻辑。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类(没有实例化一个类),覆盖其中的方法 。
Spring AOP应用场景
性能检测,访问控制,日志管理,事务等。
默认的策略是若是目标类实现接口,则使用JDK动态代理技术,若是目标对象没有实现接口,则默认会采用CGLIB代理。
SpringMVC运行原理
1.客户端请求提交到DispatcherServlet
2.由DispatcherServlet控制器查询HandlerMapping,找到并分发到指定的Controller中。
3.Controller调用业务逻辑处理后,返回ModelAndView。
4.DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图。
5.视图负责将结果显示到客户端。
一个Http请求
DNS域名解析 --> 发起TCP的三次握手 --> 创建TCP链接后发起http请求 --> 服务器响应http请求,浏览器获得html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户。
设计存储海量数据的存储系统:设计一个叫“中间层”的一个逻辑层,在这个层,将数据库的海量数据抓出来,作成缓存,运行在服务器的内存中,同理,当有新的数据到来,也先作成缓存,再想办法,持久化到数据库中,这是一个简单的思路。主要的步骤是负载均衡,将不一样用户的请求分发到不一样的处理节点上,而后先存入缓存,定时向主数据库更新数据。读写的过程采用相似乐观锁的机制,能够一直读(在写数据的时候也能够),可是每次读的时候会有个版本的标记,若是本次读的版本低于缓存的版本,会从新读数据,这样的状况并很少,能够忍受。
Session与Cookie:Cookie可让服务端跟踪每一个客户端的访问,可是每次客户端的访问都必须传回这些Cookie,若是Cookie不少,则无形的增长了客户端与服务端的数据传输量,而Session则很好地解决了这个问题,同一个客户端每次和服务端交互时,将数据存储经过Session到服务端,不须要每次都传回全部的Cookie值,而是传回一个ID,每一个客户端第一次访问服务器生成的惟一的ID,客户端只要传回这个ID就好了,这个ID一般为NAME为JSESSIONID的一个Cookie。这样服务端就能够经过这个ID,来将存储到服务端的KV值取出了。
Session和Cookie的超时问题,Cookie的安全问题
分布式Session框架
1.配置服务器,Zookeeper集群管理服务器能够统一管理全部服务器的配置文件
2.共享这些Session存储在一个分布式缓存中,能够随时写入和读取,并且性能要很好,如Memcache,Tair。
3.封装一个类继承自HttpSession,将session存入到这个类中而后再存入分布式缓存中
4因为Cookie不能跨域访问,要实现Session同步,要同步SessionID写到不一样域名下。
适配器模式:将一个接口适配到另外一个接口,Java I/O中InputStreamReader将Reader类适配到InputStream,从而实现了字节流到字符流的准换。
装饰者模式:保持原来的接口,加强原来有的功能。
FileInputStream 实现了InputStream的全部接口,BufferedInputStreams继承自FileInputStream是具体的装饰器实现者,将InputStream读取的内容保存在内存中,而提升读取的性能。
Spring事务配置方法:
1.切点信息,用于定位实施事物切面的业务类方法
2.控制事务行为的事务属性,这些属性包括事物隔离级别,事务传播行为,超时时间,回滚规则。
Spring经过aop/tx Schema 命名空间和@Transaction注解技术来进行声明式事物配置。
Mybatis
每个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心。首先用字节流经过Resource将配置文件读入,而后经过SqlSessionFactoryBuilder().build方法建立SqlSessionFactory,而后再经过sqlSessionFactory.openSession()方法建立一个sqlSession为每个数据库事务服务。
经历了Mybatis初始化 -->建立SqlSession -->运行SQL语句 返回结果三个过程。
Servlet和Filter的区别:
整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有以下几个用处:
Filter能够进行对特定的url请求和相应作预处理和后处理。
在HttpServletRequest到达Servlet以前,拦截客户的HttpServletRequest。
根据须要检查HttpServletRequest,也能够修改HttpServletRequest头和数据。
在HttpServletResponse到达客户端以前,拦截HttpServletResponse。
根据须要检查HttpServletResponse,也能够修改HttpServletResponse头和数据。
实际上Filter和Servlet极其类似,区别只是Filter不能直接对用户生成响应。实际上Filter里doFilter()方法里的代码就是从多个Servlet的service()方法里抽取的通用代码,经过使用Filter能够实现更好的复用。
Filter和Servlet的生命周期:
1.Filter在web服务器启动时初始化
2.若是某个Servlet配置了 <load-on-startup >1 </load-on-startup >,该Servlet也是在Tomcat(Servlet容器)启动时初始化。
3.若是Servlet没有配置<load-on-startup >1 </load-on-startup >,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。
4.每次请求, Request都会被初始化,响应请求后,请求被销毁。
5.Servlet初始化后,将不会随着请求的结束而注销。
6.关闭Tomcat时,Servlet、Filter依次被注销。
HashMap与HashTable的区别。
一、HashMap是非线程安全的,HashTable是线程安全的。
二、HashMap的键和值都容许有null值存在,而HashTable则不行。
三、由于线程安全的问题,HashMap效率比HashTable的要高。
HashMap的实现机制:
1.维护一个每一个元素是一个链表的数组,并且链表中的每一个节点是一个Entry[]键值对的数据结构。
2.实现了数组+链表的特性,查找快,插入删除也快。
3.对于每一个key,他对应的数组索引下标是 int i = hash(key.hashcode)&(len-1);
4.每一个新加入的节点放在链表首,而后该新加入的节点指向原链表首。
HashMap和TreeMap区别
HashMap冲突
HashMap,ConcurrentHashMap与LinkedHashMap的区别
1.ConcurrentHashMap是使用了锁分段技术技术来保证线程安全的,锁分段技术:首先将数据分红一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其余段的数据也能被其余线程访问。
2.ConcurrentHashMap 是在每一个段(segment)中线程安全的。
3.LinkedHashMap维护一个双链表,能够将里面的数据按写入的顺序读出。
ConcurrentHashMap应用场景
1:ConcurrentHashMap的应用场景是高并发,可是并不能保证线程安全,而同步的HashMap和HashMap的是锁住整个容器,而加锁以后ConcurrentHashMap不须要锁住整个容器,只须要锁住对应的Segment就行了,因此能够保证高并发同步访问,提高了效率。
2:能够多线程写。
ConcurrentHashMap把HashMap分红若干个Segmenet
1.get时,不加锁,先定位到segment而后在找到头结点进行读取操做。而value是volatile变量,因此能够保证在竞争条件时保证读取最新的值,若是读到的value是null,则可能正在修改,那么久调用ReadValueUnderLock函数,加锁保证读到的数据是正确的。
2.Put时会加锁,一概添加到hash链的头部。
3.Remove时也会加锁,因为next是final类型不可改变,因此必须把删除的节点以前的节点都复制一遍。
4.ConcurrentHashMap容许多个修改操做并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对Hash表的不一样Segment进行的修改。
ConcurrentHashMap的应用场景是高并发,可是并不能保证线程安全,而同步的HashMap和HashTable的是锁住整个容器,而加锁以后ConcurrentHashMap不须要锁住整个容器,只须要锁住对应的segment就行了,因此能够保证高并发同步访问,提高了效率。
Vector和ArrayList的区别
ExecutorService service = Executors....
ExecutorService service = new ThreadPoolExecutor()
ExecutorService service = new ScheduledThreadPoolExecutor();
ThreadPoolExecutor源码分析
线程池自己的状态:
1.2 ThreadPoolExecutor 的内部工做原理
有了以上定义好的数据,下面来看看内部是如何实现的 。 Doug Lea 的整个思路总结起来就是 5 句话:
1.若是当前池大小 poolSize 小于 corePoolSize ,则建立新线程执行任务。
2.若是当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
3.若是当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则建立新线程执行任务。
4.若是当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
5.线程池里的每一个线程执行完任务后不会马上退出,而是会去检查下等待队列里是否还有线程任务须要执行,若是在 keepAliveTime 里等不到新的任务了,那么线程就会退出。
Executor包结构
CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,而后在新的容器里面写,写完以后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,因此能够进行并发的读,但这是一种弱一致性的策略。
使用场景:CopyOnWriteArrayList适合使用在读操做远远大于写操做的场景里,好比缓存。
Linux经常使用命令:cd,cp,mv,rm,ps(进程),tar,cat(查看内容),chmod,vim,find,ls
死锁的必要条件
1.互斥 至少有一个资源处于非共享状态
2.占有并等待
3.非抢占
4.循环等待
解决死锁,第一个是死锁预防,就是不让上面的四个条件同时成立。二是,合理分配资源。
三是使用银行家算法,若是该进程请求的资源操做系统剩余量能够知足,那么就分配。
进程间的通讯方式
1.管道( pipe ):管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程关系。
2.有名管道 (named pipe) : 有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。
3.信号量( semophore ) : 信号量是一个计数器,能够用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。
4.消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5.信号 ( sinal ) : 信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。
6.共享内存( shared memory ) :共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的 IPC 方式,它是针对其余进程间通讯方式运行效率低而专门设计的。它每每与其余通讯机制,如信号量,配合使用,来实现进程间的同步和通讯。
7.套接字( socket ) : 套解口也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样机器间的进程通讯。
数据库事务是指做为单个逻辑工做单元执行的一系列操做。
Hibernate的一级缓存是由Session提供的,所以它只存在于Session的生命周期中,当程序调用save(),update(),saveOrUpdate()等方法 及调用查询接口list,filter,iterate时,如Session缓存中还不存在相应的对象,Hibernate会把该对象加入到一级缓存中,当Session关闭的时候缓存也会消失。
Hibernate的一级缓存是Session所内置的,不能被卸载,也不能进行任何配置一级缓存采用的是key-value的Map方式来实现的,在缓存实体对象时,对象的主关键字ID是Map的key,实体对象就是对应的值。
Hibernate二级缓存:把得到的全部数据对象根据ID放入到第二级缓存中。Hibernate二级缓存策略,是针对于ID查询的缓存策略,删除、更新、增长数据的时候,同时更新缓存。