摘要:最近和女朋友聊天,说个人工做须要做出调整,当前状态下压力太大,急需经过提供自身的专业技能来做出改变,因此便有了这个基础知识的整理。原本这个帖子是发布在简书的,由于考虑到简书比较好编辑和阅览,可是当我发布到简书后,女朋友居然惊讶和确定我终于开始写博客了,因而比较汗颜,就仍是回归发布到这里来吧。后续针对这些基础知识作更深次,更全面的研究,本基础的一些问题来源:http://blog.csdn.net/exceptional_derek/article/details/69525715html
答:HashMap是基于哈希表的Map接口的非同步实现。此实现提供全部可选的映射操做,并容许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变,HashMap其实是一个“链表散列”的数据结构,即数组和链表的结合体。咱们往HashMap中put元素的时候,先根据key的hashCode从新计算hash值,根据hash值获得这个元素在数组中的位置(即下标),若是数组该位置上已经存放有其余元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最早加入的放在链尾。若是数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。对HashMap的操做不是线程安全的,经过观察源码发现,当多个线程在某一个时刻同时对HashMap作结构性的修改,咱们能够看到整个方法实现中没有任何的同步机制,那么存在一个线程获取或者修改数据结构时,存在另一个线程获取了一个错误的结果。jdk8对hashMap的数据结构的改变有个调整,当数组达到必定的阈值时,bucket就会由链表转换为红黑树的方式进行存储,而不是进行table的扩容。java
答:首先String类是用final关键字修饰,这说明String不可继承,String类的成员字段value是个char[]数组,并且是用final修饰的。final修饰的字段建立之后就不可改变。不可变的好处:1.1.参考java字符串池的设计模式。好比两个字符串值相等的变量,他们只会生成一个对象放到常量池中,而后两个变量都指向它,提高效率。1.2.安全性,若是String类能够被修改,那么在多线程的状况下会形成安全漏洞。2.1 StringBuilder和StringBuffer的区别:他们都是建立字符串的经常使用类,长度都是能够扩充的,实现了CharSequence接口。StringBuilder非线程安全,StringBuffer线程安全,因此一般在单线程环境下能够考虑是用StringBuilder来提高速度和效率,而在多线程的环境下则应该使用SringBuffer来保证线程安全。node
答:反射的机制是在编译时并不肯定的哪一个类被jvm加载,在程序运行的时候才加载、探知、自审。动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现加强,但由于采用的是继承,因此不能对final修饰的类进行代理。二者区别:jdk的代理是利用反射生成字节码,并生成对象,前提是只能代理实现了接口的类,cglib是直接修改目标类的字节码生成对象,由于原理是继承,因此不能对final修饰的类进行代理。http://rejoy.iteye.com/blog/1627405https://my.oschina.net/tearsky/blog/635321mysql
答:自动装箱是将内置类型转换为对应的包装类型,在自动装箱的过程当中,程序会建立一个包装类型的对象,而后将该变量指向这个新建立的对象,完成装箱操做。程序员
答:redis
一、抽象类和接口都不能直接实例化,若是要实例化,抽象类变量必须指向实现全部抽象方法的子类对象,接口变量必须指向实现全部接口方法的类对象。算法
二、抽象类要被子类继承,接口要被类实现。spring
三、接口只能作方法申明,抽象类中能够作方法申明,也能够作方法实现。sql
四、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。数据库
五、抽象类里的抽象方法必须所有被子类所实现,若是子类不能所有实现父类抽象方法,那么该子类只能是抽象类。一样,一个实现接口的时候,如不能所有实现接口方法,那么该类也只能为抽象类。
六、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果。
七、抽象类里能够没有抽象方法。
八、若是一个类里有抽象方法,那么这个类只能是抽象类。
九、抽象方法要被实现,因此不能是静态的,也不能是私有的。
十、接口可继承接口,并可多继承接口,但类只能单根继承。
答:ConcurrentHashMap容许多个修改操做并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不一样部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不一样的部分,每一个段其实就是一个小的hash table,它们有本身的锁。只要多个修改操做发生在不一样的段上,它们就能够并发进行。
改进一:取消segments字段,直接采用transient volatile HashEntry[] table保存数据,采用table数组元素做为锁,从而实现了对每一行数据进行加锁,进一步减小并发冲突的几率。
改进二:将原先table数组+单向链表的数据结构,变动为table数组+单向链表+红黑树的结构。对于hash表来讲,最核心的能力在于将key hash以后能均匀的分布在数组中。若是hash以后散列的很均匀,那么table数组中的每一个队列长度主要为0或者1。但实际状况并不是老是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,可是在数据量过大或者运气不佳的状况下,仍是会存在一些队列长度过长的状况,若是仍是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);所以,对于个数超过8(默认值)的列表,查看源码应该是16,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度能够下降到O(logN),能够改进性能。当获取modelcount时,会全锁来统计个数。
详细讲解:http://www.importnew.com/22007.html
http://nanguocoffee.iteye.com/blog/907824
答:HashSet中add方法调用的是底层HashMap中的put()方法,而若是是在HashMap中调用put,首先会判断key是否存在,若是key存在则修改value值,若是key不存在这插入这个key-value。而在set中,由于value值没有用,也就不存在修改value值的说法,所以往HashSet中添加元素,首先判断元素(也就是key)是否存在,若是不存在这插入,若是存在着不插入,这样HashSet中就不存在重复值。
答:对传统的、基本的GC实现来讲,因为它们在GC的整个工做过程当中都要“stop-the-world”,若是能想办法缩短GC一次工做的时间长度就是件重要的事情。若是说收集整个GC堆耗时太长,那不如只收集其中的一部分?因而就有好几种不一样的划分(partition)GC堆的方式来实现部分收集,而分代式GC就是这其中的一个思路。
-Xmx2g //JVM最大容许分配的堆内存,按需分配
-Xms2g //JVM初始分配的堆内存,通常和Xmx配置成同样以免每次gc后JVM从新分配内存。-Xmn256m //年轻代内存大小,整个JVM内存=年轻代 + 年老代 + 持久代年轻代分三个区, 分别是enden区和两个survivor区。
1.可见性:JMM提供了volatile变量定义、final、synchronized块来保证可见性。
2.有序性:这个概念是相对而言的,若是在本线程内,全部的操做都是有序的,若是在一个线程观察另外一个线程,全部的操做都是无序的,前句是“线程内表现为串行行为”,后句是“指令的重排序”和“工做内存和主内存同步延迟”现象,模型提供了volatile和synchronized来保证线程之间操做的有序性。
3.重排序:在执行程序时为了提升性能,编译器和处理器经常会对指令作重排序(编译器、处理器),就是由于这些重排序,因此可能会致使多线程程序出现内存可见性问题(数据安全问题)和有序性问题。可见性、原子性、有序性.
答:1.被static修饰的变量属于类变量,能够经过类名.变量名直接引用,而不须要new出一个类来。
2.被static修饰的方法属于类方法,能够经过类名.方法名直接引用,而不须要new出一个类来。
3.静态块也是static的重要应用之一。也是用于初始化一个类的时候作操做用的,和静态变量、静态方法同样,静态块里面的代码只执行一次,且只在初始化类的时候执行。
这个用得相对比前面的用法少多了,static通常状况下来讲是不能够修饰类的, 若是static要修饰一个类,说明这个类是一个静态内部类(注意static只能修饰一个内部类),也就是匿名内部类。
4.import static是JDK1.5以后的新特性,这两个关键字连用能够指定导入某个类中的指定静态资源,而且不须要使用类名.资源名,能够直接使用资源名。
1.对象锁钥匙只能有一把才能互斥,才能保证共享变量的惟一性
2.在静态方法上的锁,和 实例方法上的锁,默认不是一样的,若是同步须要制定两把锁同样。
3.关于同一个类的方法上的锁,来自于调用该方法的对象,若是调用该方法的对象是相同的,那么锁必然相同,不然就不相同。好比 new A().x() 和 newA().x(),对象不一样,锁不一样,若是A的单利的,就能互斥。
4.静态方法加锁,能和全部其余静态方法加锁的 进行互斥。
5.静态方法加锁,和xx.class 锁效果同样,直接属于类的。
答:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。主要是经过获取锁标记来获取对该资源的使用权限,当对象调用了start()进入到就绪状态,进入就绪后,当该对象被操做系统选中,得到CPU时间片就会进入运行状态;接下来的状态切换就会比较复杂,主要经过线程调用不一样的方法,就会切换不一样的运行状态。
答:volatile让变量每次在使用的时候,都从主存中取。(1.将当前处理器缓存行的数据会写回到系统内存,2.这个写回内存的操做会引发在其余CPU里缓存了该内存地址的数据无效。)而不是从各个线程的“工做内存”。volatile具备synchronized关键字的“可见性”,可是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性,volatile变量对于每次使用,线程都能获得当前volatile变量的最新值。可是volatile变量并不保证并发的正确性。
答:类要成为线程安全的,首先必须在单线程环境中有正确的行为。若是一个类实现正确(这是说它符合规格说明的另外一种方式),那么没有一种对这个类的对象的操做序列(读或者写公共字段以及调用公共方法)可让对象处于无效状态,观察到对象处于无效状态、或者违反类的任何不可变量、前置条件或者后置条件的状况。此外,一个类要成为线程安全的,在被多个线程访问时,无论运行时环境执行这些线程有什么样的时序安排或者交错,它必须仍然有如上所述的正确行为,而且在调用的代码中没有任何额外的同步。其效果就是,在全部线程看来,对于线程安全对象的操做是以固定的、全局一致的顺序发生的。正确性与线程安全性之间的关系很是相似于在描述 ACID(原子性、一致性、独立性和持久性)事务时使用的一致性与独立性之间的关系:从特定线程的角度看,由不一样线程所执行的对象操做是前后(虽然顺序不定)而不是并行执行的。
答: java容许多线程并发控制,当多个线程同时操做一个可共享的资源变量时(如数据的增删改查), 将会致使数据不许确,相互之间产生冲突,所以加入同步锁以免在该线程没有完成操做以前,被其余线程的调用, 从而保证了该变量的惟一性和准确性。
同步的实现方式总共分为七种:
1.同步方法 : 即有synchronized关键字修饰的方法,因为java的每一个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,须要得到内置锁,不然就处于阻塞状态。
2.同步代码块:即有synchronized关键字修饰的语句块,被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
3.使用特殊域变量(volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制。
b.使用volatile修饰域至关于告诉虚拟机该域可能会被其余线程更新。
c.所以每次使用该域就要从新计算,而不是使用寄存器中的值。
d.volatile不会提供任何原子操做,它也不能用来修饰final类型的变量。
4.使用重入锁实现线程同步.
java.util.concurrent包下的ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具备相同的基本行为和语义,而且扩展了其能力.
5.使用局部变量实现线程同步,若是使用ThreadLocal管理变量,则每个使用该变量的线程都得到该变量的副本, 副本之间相互独立,这样每个线程均可以随意修改本身的变量副本,而不会对其余线程产生影响
6.使用阻塞队列实现线程同步,前面5种同步方式都是在底层实现的线程同步,可是咱们在实际开发当中,应当尽可能远离底层结构。使用javaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。使用LinkedBlockingQueue来实现线程的同步, LinkedBlockingQueue是一个基于已链接节点的,范围任意的blocking queue。队列是先进先出的顺序(FIFO)。
7.使用原子变量实现线程同步,须要使用线程同步的根本缘由在于对普通变量的操做不是原子的。原子操做就是指将读取变量值、修改变量值、保存变量值当作一个总体来操做,即-这几种行为要么同时完成,要么都不完成。在java的util.concurrent.atomic包中提供了建立了原子类型变量的工具类,使用该类能够简化线程同步。其中AtomicInteger 表能够用原子方式更新int的值,可用在应用程序中(如以原子方式增长的计数器),但不能用于替换Integer;可扩展Number,容许那些处理机遇数字类的工具和实用工具进行统一访问。
答:ThreadLocal提供了set和get访问器用来访问与当前线程相关联的线程局部变量。当线程中的threadlocalmap是null的时候,会调用createmap建立一个map。同时根据函数参数设置上初始值。也就是说,当前线程的threadlocalmap是在第一次调用set的时候建立map而且设置上相应的值的。在ThreadLocal的set函数中,能够看到,其中的map.set(this, value);把当前的threadlocal传入到map中做为键,也就是说,在不一样的线程的threadlocals变量中,都会有一个以你所声明的那个线程局部变量threadlocal做为键的key-value。假设说声明了N个这样的线程局部变量变量,那么在线程的ThreadLocalMap中就会有n个分别以你的线程局部变量做为key的键值对。
答:每一个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的全部权,过程以下:
一、若是monitor的进入数为0,则该线程进入monitor,而后将进入数设置为1,该线程即为monitor的全部者。
二、若是线程已经占有该monitor,只是从新进入,则进入monitor的进入数加1.
3.若是其余线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再从新尝试获取monitor的全部权。
对于方法的同步,方法的同步并无经过指令monitorenter和monitorexit来完成(理论上其实也能够经过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,若是设置了,执行线程将先获取monitor,获取成功以后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其余任何线程都没法再得到同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需经过字节码来完成。
答:sleep()方法致使了程序暂停执行指定的时间,让出cpu给其余线程,可是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程当中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。
答:1.newFixedThreadPool建立一个指定工做线程数量的线程池。每当提交一个任务就建立一个工做线程,若是工做线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
2.newCachedThreadPool建立一个可缓存的线程池。这种类型的线程池特色是:
1).工做线程的建立数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).若是长时间没有往线程池中提交任务,即若是工做线程空闲了指定的时间(默认为1分钟),则该工做线程将自动终止。终止后,若是你又提交了新的任务,则线程池从新建立一个工做线程。
3.newSingleThreadExecutor建立一个单线程化的Executor,即只建立惟一的工做者线程来执行任务,若是这个线程异常结束,会有另外一个取代它,保证顺序执行(我以为这点是它的特点)。单工做线程最大的特色是可保证顺序地执行各个任务,而且在任意给定的时间不会有多个线程是活动的。
4.newScheduleThreadPool建立一个定长的线程池,并且支持定时的以及周期性的任务执行,相似于Timer。
总结:
一.FixedThreadPool是一个典型且优秀的线程池,它具备线程池提升程序效率和节省建立线程时所耗的开销的优势。可是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工做线程,还会占用必定的系统资源。
二.CachedThreadPool的特色就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工做线程,从而释放工做线程所占用的资源。可是,但当出现新任务时,又要建立一新的工做线程,又要必定的系统开销。而且,在使用CachedThreadPool时,必定要注意控制任务的数量,不然,因为大量线程同时运行,颇有会形成系统瘫痪。
答:一个线程主要包括如下4个部分:
1.线程池管理器(ThreadPool):用于建立并管理线程池,包括 建立线程池,销毁线程池,添加新任务;
2.工做线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,能够循环的执行任务;
3.任务接口(Task):每一个任务必须实现的接口,以供工做线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工做,任务的执行状态等;
4.任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
ThreadPoolExecutor类可设置的参数主要有:
corePoolSize
核心线程数,核心线程会一直存活,即便没有任务须要处理。当线程数小于核心线程数时,即便现有的线程空闲,线程池也会优先建立新线程来处理任务,而不是直接交给现有的线程处理。
核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认状况下不会退出。
maxPoolSize
当线程数大于或等于核心线程,且任务队列已满时,线程池会建立新的线程,直到线程数量达到maxPoolSize。若是线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
keepAliveTime
当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。若是allowCoreThreadTimeout设置为true,则全部线程均会退出直到线程数量为0。
allowCoreThreadTimeout
是否容许核心线程空闲退出,默认值为false。
queueCapacity
任务队列容量。从maxPoolSize的描述上能够看出,任务队列的容量会影响到线程的变化,所以任务队列的长度也须要恰当的设置。
答:Semaphore【ˈseməfɔ:(r)】就是一个信号量,它的做用是限制某段代码块的并发数。Semaphore有一个构造函数,能够传入一个int型整数n,表示某段代码最多只有n个线程能够访问,若是超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此能够看出若是Semaphore构造函数中传入的int型整数n=1,至关于变成了一个synchronized了。
FutureTask 表示一个异步运算的任务。FutureTask里面能够传入一个Callable的具体实现类,能够对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操做。固然,因为FutureTask也是Runnable接口的实现类,因此FutureTask也能够放入线程池中。
答:execute(Runnable x) 没有返回值。能够执行任务,但没法判断任务是否成功完成。——实现Runnable接口
submit(Runnable x) 返回一个future。能够用这个future来判断任务是否成功完成。——实现Callable接口。
答:
cancel方法主要是是否可中断来设置state及中断状态。
get()方法主要是调用awaitdone方法来阻塞来等待结果,而后根据最终状态来返回结果或者抛出异常。
isDone()任务是否已经完成。须要注意的是若是任务正常终止、异常或取消,都将返回true
isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
答:1.继承Thread类建立线程类
2.经过Runnable接口建立线程类
3.经过Callable和Future建立线程
答:http://blog.jobbole.com/108571/
和线程相关的更多问题能够移步这里:http://www.jianshu.com/p/64dae9b00eed
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就能够获得索引的本质:索引是一种数据结构。 数据库查询是数据库的主要功能之一,最基本的查询算法是顺序查找(linear search)时间复杂度为O(n),显然在数据量很大时效率很低。优化的查找算法如二分查找(binary search)、二叉树查找(binary tree search)等,虽然查找效率提升了。可是各自对检索的数据都有要求:二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,可是数据自己的组织结构不可能彻底知足各类数据结构(例如,理论上不可能同时将两列都按顺序进行组织)。因此,在数据以外,数据库系统还维护着知足特定查找算法的数据结构。这些数据结构以某种方式引用(指向)数据,这样就能够在这些数据结构上实现高级查找算法。这种数据结构就是索引。
通常来讲,索引自己也很大,不可能所有存储在内存中,所以索引每每以索引文件的形式存储的磁盘上。这样的话,索引查找过程当中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,因此评价一个数据结构做为索引的优劣最重要的指标就是在查找过程当中磁盘I/O操做次数的渐进复杂度。换句话说,索引的结构组织要尽可能减小查找过程当中磁盘I/O的存取次数。
从使用磁盘I/O次数评价索引结构的优劣性:根据B-Tree的定义,可知检索一次最多须要访问h个结点。数据库系统的设计者巧妙的利用了磁盘预读原理,将一个结点的大小设为等于一个页面,这样每一个结点只须要一次I/O就能够彻底载入。为了达到这个目的,在实际实现B-Tree还须要使用以下技巧:每次新建结点时,直接申请一个页面的空间,这样能够保证一个结点的大小等于一个页面,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。B-Tree中一次检索最多须要h-1次I/O(根结点常驻内存),渐进复杂度为O(h)=O(logdN)。通常实际应用中,出读d是很是大的数字,一般超过100,所以h很是小。综上所述,用B-Tree做为索引结构效率是很是高的。而红黑树结构,h明显要深得多。因为逻辑上很近的结点(父子结点)物理上可能离得很远,没法利用局部性原理。因此即便红黑树的I/O渐进复杂度也为O(h),可是查找效率明显比B-Tree差得多。B+Tree更适合外存索引,是和内结点出度d有关。从上面分析能够看到,d越大索引的性能越好,而出度的上限取决于结点内key和data的大小:dmax=floor(pagesize/(keysize+datasize+pointsize))。floor表示向下取整。因为B+Tree内结点去掉了data域,所以能够拥有更大的出度,拥有更好的性能。
参考:http://www.cnblogs.com/tgycoder/p/5410057.html
答:事物隔离级别由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable。读取未提交的数据称之为脏读,一个事务只能看见已经提交事务所作的改变称之为不可重复读,指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行,称之为幻读。
下图列的是各个事物隔离级别下可能出现的脏读、不可重复读、幻读图:
Read Uncommitted(读取未提交内容)
在该隔离级别,全部事务均可以看到其余未提交事务的执行结果。本隔离级别不多用于实际应用,由于它的性能也不比其余级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它知足了隔离的简单定义:一个事务只能看见已经提交事务所作的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),由于同一事务的其余实例在该实例处理其间可能会有新的commit,因此同一select可能返回不一样结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行。不过理论上,这会致使另外一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每一个读的数据行上加上共享锁。在这个级别,可能致使大量的超时现象和锁竞争。
参考:http://xm-king.iteye.com/blog/770721
答:MVCC全称是Multi-Version Concurrent Control,即多版本并发控制。在MVCC协议下,每一个读操做会看到一个一致性的snapshot,而且能够实现非阻塞的读。MVCC容许数据具备多个版本,这个版本能够是时间戳或者是全局递增的事务ID,在同一个时间点,不一样的事务看到的数据是不一样的。在进行写操做时,将数据copy一份,不会影响原有数据,而后进行修改,修改完成后原子替换掉旧的数据,而读操做只会读取原有数据。经过这种方式实现写操做不会阻塞读操做,从而优化读效率。而写操做之间是要互斥的,而且每次写操做都会有一次copy,因此只适合读大于写的状况。
优点:使用MVCC多版本并发控制比锁定模型的主要优势是在MVCC里, 对检索(读)数据的锁要求与写数据的锁要求不冲突, 因此读不会阻塞写,而写也从不阻塞读。
答:共享读锁,独占写锁。根据数据引擎的不一样,锁的类型也不同,对于innodb
原子性、一致性、隔离性、持久性。
答:1.查询条件带上索引,
答:一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分相似。一致性哈希修正了CARP使用的简 单哈希算法带来的问题,使得分布式哈希(DHT)能够在P2P环境中真正获得应用。
一致性hash算法提出了在动态变化的Cache环境中,断定哈希算法好坏的四个定义:
一、平衡性(Balance):平衡性是指哈希的结果可以尽量分布到全部的缓冲中去,这样可使得全部的缓冲空间都获得利用。不少哈希算法都可以知足这一条件。
二、单调性(Monotonicity):单调性是指若是已经有一些内容经过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应可以保证原有已分配的内容能够被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其余缓冲区。
三、分散性(Spread):在分布式环境中,终端有可能看不到全部的缓冲,而是只能看到其中的一部分。当终端但愿经过哈希过程将内容映射到缓冲上时,因为不一样终端所见的缓冲范围有可能不一样,从而致使哈希的结果不一致,最终的结果是相同的内容被不一样的终端映射到不一样的缓冲区中。这种状况显然是应该避免的,由于它致使相同内容被存储到不一样缓冲中去,下降了系统存储的效率。分散性的定义就是上述状况发生的严重程度。好的哈希算法应可以尽可能避免不一致的状况发生,也就是尽可能下降分散性。
四、负载(Load):负载问题其实是从另外一个角度看待分散性问题。既然不一样的终端可能将相同的内容映射到不一样的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不一样的用户映射为不一样 的内容。与分散性同样,这种状况也是应当避免的,所以好的哈希算法应可以尽可能下降缓冲的负荷。
在分布式集群中,对机器的添加删除,或者机器故障后自动脱离集群这些操做是分布式集群管理最基本的功能。若是采用经常使用的hash(object)%N算法,那么在有机器添加或者删除后,不少原有的数据就没法找到了,这样严重的违反了单调性原则。接下来主要讲解一下一致性哈希算法是如何设计的:
环形Hash空间
按照经常使用的hash算法来将对应的key哈希到一个具备2^32次方个桶的空间中,即0~(2^32)-1的数字空间中。如今咱们能够将这些数字头尾相连,想象成一个闭合的环形。以下图
将数据经过hash算法处理后映射到环上
如今咱们将object一、object二、object三、object4四个对象经过特定的Hash函数计算出对应的key值,而后散列到Hash环上。以下图:
Hash(object1) = key1;
Hash(object2) = key2;
Hash(object3) = key3;
Hash(object4) = key4;
将机器经过hash算法处理后映射到环上
在采用一致性哈希算法的分布式集群中将新的机器加入,其原理是经过使用与对象存储同样的Hash算法将机器也映射到环中(通常状况下对机器的hash计算是采用机器的IP或者机器惟一的别名做为输入值),而后以顺时针的方向计算,将全部对象存储到离本身最近的机器中。
假设如今有NODE1,NODE2,NODE3三台机器,经过Hash算法获得对应的KEY值,映射到环中,其示意图以下:
Hash(NODE1) = KEY1;
Hash(NODE2) = KEY2;
Hash(NODE3) = KEY3;
经过上图能够看出对象与机器处于同一哈希空间中,这样按顺时针转动object1存储到了NODE1中,object3存储到了NODE2中,object二、object4存储到了NODE3中。在这样的部署环境中,hash环是不会变动的,所以,经过算出对象的hash值就能快速的定位到对应的机器中,这样就能找到对象真正的存储位置了。
机器的删除与添加
普通hash求余算法最为不妥的地方就是在有机器的添加或者删除以后会形成大量的对象存储位置失效,这样就大大的不知足单调性了,下面来分析一下一致性哈希算法是如何处理的。
1. 节点(机器)的删除
以上面的分布为例,若是NODE2出现故障被删除了,那么按照顺时针迁移的方法,object3将会被迁移到NODE3中,这样仅仅是object3的映射位置发生了变化,其它的对象没有任何的改动。以下图:
2. 节点(机器)的添加
若是往集群中添加一个新的节点NODE4,经过对应的哈希算法获得KEY4,并映射到环中,以下图:
经过按顺时针迁移的规则,那么object2被迁移到了NODE4中,其它对象还保持这原有的存储位置。经过对节点的添加和删除的分析,一致性哈希算法在保持了单调性的同时,仍是数据的迁移达到了最小,这样的算法对分布式集群来讲是很是合适的,避免了大量数据迁移,减少了服务器的的压力。
平衡性
根据上面的图解分析,一致性哈希算法知足了单调性和负载均衡的特性以及通常hash算法的分散性,但这还并不能当作其被普遍应用的起因,由于还缺乏了平衡性。下面将分析一致性哈希算法是如何知足平衡性的。hash算法是不保证平衡的,如上面只部署了NODE1和NODE3的状况(NODE2被删除的图),object1存储到了NODE1中,而object二、object三、object4都存储到了NODE3中,这样就照成了很是不平衡的状态。在一致性哈希算法中,为了尽量的知足平衡性,其引入了虚拟节点。
——“虚拟节点”( virtual node )是实际节点(机器)在 hash 空间的复制品( replica ),一实际个节点(机器)对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以hash值排列。
以上面只部署了NODE1和NODE3的状况(NODE2被删除的图)为例,以前的对象在机器上的分布很不均衡,如今咱们以2个副本(复制个数)为例,这样整个hash环中就存在了4个虚拟节点,最后对象映射的关系图以下:
根据上图可知对象的映射关系:object1->NODE1-1,object2->NODE1-2,object3->NODE3-2,object4->NODE3-1。经过虚拟节点的引入,对象的分布就比较均衡了。那么在实际操做中,正真的对象查询是如何工做的呢?对象从hash到虚拟节点到实际节点的转换以下图:
“虚拟节点”的hash计算能够采用对应节点的IP地址加数字后缀的方式。例如假设NODE1的IP地址为192.168.1.100。引入“虚拟节点”前,计算 cache A 的 hash 值:
Hash(“192.168.1.100”);
引入“虚拟节点”后,计算“虚拟节”点NODE1-1和NODE1-2的hash值:
Hash(“192.168.1.100#1”); // NODE1-1
Hash(“192.168.1.100#2”); // NODE1-2
转自:http://blog.csdn.net/cywosp/article/details/23397179
答:
答:0是5跟偶数相乘获得的,因此有几个0就看有几个5 50内5的倍数有10个,因此能获得10个0.可是25的倍数里有两个5,还要再加两个 总共是12个。
这部分主要根据简历以及项目的实际状况来问。
AOP的实现原理就是动态的生成代理类,代理类的执行过程为:执行咱们增长的代码(例如方法日志记录)—-> 回调原方法 ——> 增长的代码逻辑。
Spring AOP 动态代理可能采用JDK动态代理或CGlib动态生成代理类两种方式中的一种,决定用哪种方式的判断标准就是被切面的类是否有其实现的接口,若是有对应的接口,则采用JDK动态代理,不然采用CGlib字节码生成机制动态代理方式。代理模式是一种经常使用的设计模式,其目的就是为其余对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。代理类和委托类实现相同的接口,因此调用者调用代理类和调用委托类几乎感受不到差异。动态代理的意思是运行时动态生成代理实现类,因为JVM的机制,须要直接操做字节码,生成新的字节码文件,也就是.class 文件。
JDK动态代理
JDK动态代理模式采用sun的ProxyGenerator的字节码框架。要说明的是,只有实现了接口的类才能使用 JDK 动态代理技术,实现起来也比较简单。
1. 只要实现 InvocationHandler 接口,并覆写 invoke方法便可。Proxy.newProxyInstance方法用于动态生成实际生成的代理类,三个参数依次为被代理类的类加载器、被代理类所实现的接口和当前代理拦截器。
覆写的 invoke 中能够加入咱们增长的业务逻辑,而后回调原方法。
jdkProxy.bind 会生成一个实际的代理类,这个生成过程是利用的字节码生成技术,生成的代理类实现了IWorker 接口,咱们调用这个代理类的 dowork 方法的时候,实际在代理类中是调用了 JdkProxy (也就是咱们实现的这个代理拦截器)的 invoke 方法,接着执行咱们实现的 invoke 方法,也就执行了咱们加入的逻辑,从而实现了切面编程的需求。
咱们把动态生成的代理类字节码文件反编译一下,也就明白了。
CGLIB动态代理
CGlib库使用了ASM这一个轻量但高性能的字节码操做框架来转化字节码,它能够在运行时基于一个类动态生成它的子类。无论有没有接口,凡是类均可以被继承,拥有这样的特色,原则上来讲,它能够对任何类进行代码拦截,从而达到切面编程的目的。
CGlib 不须要咱们很是了解字节码文件(.class 文件)的格式,经过简单的 API 便可实现字节码操做。
基于这样的特色,CGlib 被普遍用于如 Spring AOP 等基于 代理模式的AOP框架中。
CGlib不支持final类,CGlib 的执行速度比较快,可是建立速度比较慢,因此若是两种动态代理都适用的场景下,有大量动态代理类建立的场景下,用 JDK 动态代理模式,不然能够用 CGlib 。
jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来讲,反射机制在生成类的过程当中比较高效,而asm在生成类以后的相关执行过程当中比较高效(能够经过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。若是没有上述前提,jdk动态代理不能应用。