Java 面试题 百度/参考的答案

2018-08-03 07:30:04来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

  • "a=b"和"a.equals(b)"有什么区别?
    如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,当内容相同时,返回true,所以通常需要重写该方法来提供逻辑一致性的比较。
        例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。
  • 说明ArrayList,Vector,LinkedList的存储性能和特性?
    ArrayList 和Vector他们底层的实现都是一样的,都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。
          Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。
          LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。            
          Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。
  • Overload和Override的区别,Overload的是否可以修改返回值?
    Overload是重载的意思,
    
    Override是覆盖的意思,也就是重写。
    
    重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
    
    重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。
    
    子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。
    
    子类方法的访问权限只能比父类的更大,不能更小。
    
    如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。
    重载overload的特点就是与返回值无关,只看参数列表,所以重载的方法是可以改变返回值类型的。所以,如果两个方法的参数列表完全一样,是不能通过让他们的返回值类型不同来实现重载的。我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然remove方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。
  • sleep和wait的区别?
    对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
    sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
    在调用sleep()方法的过程中,线程不会释放对象锁。
    而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
    获取对象锁进入运行状态。
  • java中WeakReference与SoftReference的区别?
    转载自:http://blog.csdn.net/zhandoushi1982/article/details/8745027
    
    在Java 1.2中就引入了java.lang.ref这个包,WeakReference就属于这个包。WeakReference是干嘛的呢,一言弊之,它是和Java中的垃圾回收相关的。如果一个对象只有WeakReference引用它,那么这个对象就可能被垃圾回收器回收。
    
            在什么场合下应用WeakReference呢?
    
    (1) 有时我们会碰到一些不能继承的类,如final class,无法继承它。假如我们要使用一个Widget类,因为某种缘故无法继承该类来加入某个功能。但是,我们必须将每个Widget对象和某个序列号关联,而Widget本身没有serial number这个属性,这时该怎么做呢?
    
            你也许已经想到,用HashMap:serialNumberMap.put(widget, widgetSerialNumber);这看起来工作的很好。但是有个问题:当我们不再需要某个Widget的serial number信息,此时应该从HashMap中将这个Entry移除,如果我们忘记了怎么办?因为HashMap中持有对这个对象的引用,这个对象永远不会被垃圾回收器回收,这就造成了内存泄漏!这意味着我们需要像没有垃圾回收功能的语言一样,手动管理内存!但是我们用的是Java。
    
    (2)另一个很常见的问题是缓存。如果使用强引用,那么我们缓存的对象就会一直滞留在内存中,不会被回收,除非我们手动的将其从缓存中移除。此外,这还需要我们决定何时从缓存中移除对象,又一个手动管理内存的问题!此时,WeakReference就显示出它的价值了。如何创建一个WeakReference呢?
      要注意的是,当调用weakReference.get()可能返回null(意味着指向的对象已经被回收)。其实,对于Widget serial number这个问题,最简单的方法是使用WeakHashMap,它的使用和普通的HashMap完全一样,不同点在于,WeakHashMap的key被实现为一种WeakReference(注意,是key而不是value),当key对象被回收后,WeakHashMap会自动将对应的entry移除。更精确的说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收。
    (3)Java中有四种类型的引用,按照强弱关系依次为:Strong Reference>Soft Reference>WeakReference> Phantom Reference。其中,我们平常用的就是Strong Reference,而Phantom Reference很少用到,那么什么是Soft Reference呢?
    
            Soft Reference和weak reference的区别是:一旦gc发现对象是weak reference可达就会把它放到ReferenceQueue中,然后等下次gc时回收它;当对象是Soft reference可达时,gc可能会向操作系统申请更多内存,而不是直接回收它,当实在没辙了才回收它。像cache系统,最适合用Soft reference。让gc来替我们决定什么时候回收对象以及回收哪些对象。差别样例如下:WeakReference的模型
    
     A obj = new A();      
    WeakReference wr = new WeakReference(obj);  
    obj = null;  
    //等待一段时间,obj对象就会被垃圾回收 
    ...   
    if (wr.get()==null) 
    {  
         System.out.println("obj 已经被清除了 ");  
    } else {   
         System.out.println("obj 尚未被清除,其信息是 "+obj.toString()); 
    }
  • SimpleDataFormat是线程非安全的,如何更好的使用而避免风险?
    详细转载https://blog.csdn.net/leonzhang1027/article/details/79480138
    我们通过看JDK源码来看看为什么SimpleDateFormat和DateFormat类不是线程安全的真正原因:
      SimpleDateFormat继承了DateFormat,在DateFormat中定义了一个protected属性的 Calendar类的对象:calendar。只是因为Calendar累的概念复杂,牵扯到时区与本地化等等,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。
    calendar.setTime(date)这条语句改变了calendar,稍后,calendar还会用到(在subFormat方法里),而这就是引发问题的根源。想象一下,在一个多线程环境下,有两个线程持有了同一个SimpleDateFormat的实例,分别调用format方法:
      线程1调用format方法,改变了calendar这个字段。
      中断来了。
      线程2开始执行,它也改变了calendar。
      又中断了。
      线程1回来了,此时,calendar已然不是它所设的值,而是走上了线程2设计的道路。如果多个线程同时争抢calendar对象,则会出现各种问题,时间不对,线程挂死等等。
      分析一下format的实现,我们不难发现,用到成员变量calendar,唯一的好处,就是在调用subFormat时,少了一个参数,却带来了这许多的问题。其实,只要在这里用一个局部变量,一路传递下去,所有问题都将迎刃而解。
      这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所以,它是有状态的。
    
      这也同时提醒我们在开发和设计系统的时候注意下一下三点:
    
      1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明
    
      2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性
    
      3.我们的类和方法在做设计的时候,要尽量设计成无状态的
     三.解决办法
    
      1.需要的时候创建新实例:
          说明:在需要用到SimpleDateFormat 的地方新建一个实例,不管什么时候,将有线程安全问题的对象由共享变为局部私有都能避免多线程问题,不过也加重了创建对象的负担。在一般情况下,这样其实对性能影响比不是很明显的。
         2.使用同步:同步SimpleDateFormat对象
        说明:当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,多线程并发量大的时候会对性能有一定的影响
        3.使用ThreadLocal:
        说明:使用ThreadLocal, 也是将共享变量变为独享,线程独享肯定能比方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比较高的情况下,一般推荐使用这种方法。
     4.抛弃JDK,使用其他类库中的时间格式化类:
      1.使用Apache commons 里的FastDateFormat,宣称是既快又线程安全的SimpleDateFormat, 可惜它只能对日期进行format, 不能对日期串进行解析。
      2.使用Joda-Time类库来处理时间相关问题
      做一个简单的压力测试,方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系统方法一和方法二就可以满足,所以说在这个点很难成为你系统的瓶颈所在。从简单的角度来说,建议使用方法一或者方法二,如果在必要的时候,追求那么一点性能提升的话,可以考虑用方法三,用ThreadLocal做缓存。
    
      Joda-Time类库对时间处理方式比较完美,建议使用。
  • 什么事线程局部变量?
    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
      ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
      线程局部变量并不是Java的新发明,很多语言(如IBM XL、FORTRAN)在语法层面就提供线程局部变量。在Java中没有提供语言级支持,而以一种变通的方法,通过ThreadLocal的类提供支持。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,这也是为什么线程局部变量没有在Java开发者中得到很好普及的原因。
    
      学习JDK中的类,首先看下JDK API对此类的描述,描述如下:
    
      该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
      API表达了下面几种观点:
      1、ThreadLocal不是线程,是线程的一个变量,你可以先简单理解为线程类的属性变量。
    
      2、ThreadLocal在类中通常定义为静态变量。
    
      3、每个线程有自己的一个ThreadLocal,它是变量的一个“拷贝”,修改它不影响其他线程。
    
      既然定义为类变量,为何为每个线程维护一个副本(姑且称为“拷贝”容易理解),让每个线程独立访问?多线程编程的经验告诉我们,对于线程共享资源(你可以理解为属性),资源是否被所有线程共享,也就是说这个资源被一个线程修改是否影响另一个线程的运行,如果影响我们需要使用synchronized同步,让线程顺序访问。
    
      ThreadLocal适用于资源共享但不需要维护状态的情况,也就是一个线程对资源的修改,不影响另一个线程的运行;这种设计是‘空间换时间’,synchronized顺序执行是‘时间换取空间’。
  • JRE,JDK,JVM,JIT之间有什么不同?
    java虚拟机(JVM)
        使用java编程语言的主要优势就是平台的独立性。你曾经想知道过java怎么实现平台的独立性吗?对,就是虚拟机,它抽象化了硬件设备,开发者和他们的程序的得以操作系统。虚拟机的职责就是处理和操作系统的交流。java不同的接口规范对任何平台都有良好的支持,因为jvm很好的实现了每个平台的规范。jvm可以理解伪代码字节码,在用户和操作系统之间建立了一层枢纽。
     
    java运行时环境(JRE)
        java运行时环境是JVM的一个超集。JVM对于一个平台或者操作系统是明确的,而JRE确实一个一般的概念,他代表了完整的运行时环境。我们在jre文件夹中看到的所有的jar文件和可执行文件都会变成运行时的一部分。事实上,运行时JRE变成了JVM。所以对于一般情况时候使用JRE,对于明确的操作系统来说使用JVM。当你下载了JRE的时候,也就自动下载了JVM。
     
    java开发工具箱(JDK)
        java开发工具箱指的是编写一个java应用所需要的所有jar文件和可执行文件。事实上,JRE是JDK的一部分。如果你下载了JDK,你会看到一个名叫JRE的文件夹在里面。JDK中要被牢记的jar文件就是tools.jar,它包含了用于执行java文档的类还有用于类签名的jar包。
     
    即时编译器(JIT)
        即时编译器是种特殊的编译器,它通过有效的把字节码变成机器码来提高JVM的效率。JIT这种功效很特殊,因为他把检测到的相似的字节码编译成单一运行的机器码,从而节省了CPU的使用。这和其他的字节码编译器不同,因为他是运行时(第一类执行的编译?)the firs of its kind to perform the compilation(从字节码到机器码)而不是在程序运行之前。正是因为这些,动态编译这个词汇才和JIT有那么紧密的关系。
    转载:https://www.cnblogs.com/xiaozhijing/p/7919455.html
  • 谈谈对设计模式的认识与理解,简单介绍一下你所掌握的设计模式?
    1,设计模式是为了解决现实的问题,是对某种问题的解决方案。
    2,“对变化点的封装”出现的很多,我认为,这点是程序可以进行扩展的编程方法。这个应该是开闭原则要达到的目标。
    3,编程原则是“道”,设计模式是“术”,使用模式的时候,一定要始终贯彻原则。
    4,设计模式是基于面向对象的语言,尤其是继承,并且用多态的方式来保证程序增加功能,不用修改原代码。(面向过程不知道能不能实现)
    5,代码的可扩展性,我觉得主要就是开闭原则所带来的效果,
    6,设计模式虽然说是模式,但是代码的实现方法其实很灵活,他对应的应该是一种问题,或者说是如何封装变化点,具体的封装方法实现起来比较灵活。
    7,程序经常会有很多需要改变的地方,到底需不需要封装变化点,根本的原则在什么地方?
    下面说说我对几个模式的认识,
    1,简单工厂模式,解决的是客户端通过大量的判断来选择相应的对象,需要进行封装的问题。特点是判断的条件需要统一,比如字符串,或者一个特定的值。使用的结果就是判断由工厂方法来做。并且可以通过继承和多态,使得相同的语句返回一系列拥有共同父类的对象。
    2,策略模式,解决的是客户端选择方法的封装方法(与之相比的是简单工厂模式封装的是对象),其实把简单工厂模式稍作修改,就是策略模式了。可以认为是用简单工厂模式返回了一系列有共同父类或者同一接口的对象,然后调用对象的接口或者虚方法。这样客户端代码只是通过工厂生成了一个对象,然后调用对象的方法,并通过多态来实现不同的程序功能。
    3,装饰模式,解决的问题是,现在有一个对象,我们想给他增加方法或者属性,怎么去做,并且可以随意修改增加方法和属性的顺序。其实我首先想到的是c#的扩展方法。感觉和扩展方法的目的差不多。
    4,工厂模式,,,正在看。怎么看怎么觉得好像在做无用功。。。。。不知道他是干嘛的。
    
    结合我现在的工作,我最近在做网页处理程序,ashx。
    我就在想这种处理程序,处理网络请求,我现在用的基本都是面向过程的,就是判断传过来什么方法,执行什么操作。用switch来跳转。
    现在我有几个疑问:
    1,这种很符合策略模式,我现在要不要使用策略模式?为什么?
    2,是不是永远都要等到问题出现了,才去引用设计模式?
    3,当问题出现的时候,可能需要修改的地方就已经很多了,这时候怎么办?
  • 有三个页面,a.jsp,b.jsp,c.jsp,流程是a.jsp->b.jsp->c.jsp,现在a.jsp提交了数据,需要在c.jsp访问,用最简单最安全的方法怎么做?hibernate或ibatis,任选一种,描述下工作原理?
    用隐藏表单域,即在b.jsp页面中用n个hidden把上一页面提交过来的信息保存下来,然后和当前一起提交,再到c.jsp里面获取 
    说明:尽量不要用session和少用session
  • 简述Spring事务的传播行为和隔离级别?
    详细转载:https://blog.csdn.net/yujin753/article/details/42242297
    传播行为:
        Required:默认的事务传播行为,表示必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务
    RequiresNew:创建新的逻辑事务,使用            
         PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)因此外部事务可以不受内部事务回滚状态的影响独立提交或者回滚。
         Supports:支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行。
         NotSupported:不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行。
    
        Mandatory:使用PROPAGATION_MANDATORY指定,如果当前有事务,使用当前事务执行,如果当前没有事务,则抛出异常(IllegalTransactionStateException)。
    
        Never:不支持事务,如果当前存在是事务则抛出IllegalTransactionStateException异常,使用PROPAGATION_NEVER指定。
    
       Nested:嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚。
    
    Nested和RequiresNew的区别: 
    RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;
    Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;
    Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务。
        实际应用中一般使用默认的事务传播行为,偶尔会用到RequiresNew和Nested方式。  
    
    隔离级别:
         1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
          另外四个与JDBC的隔离级别相对应
         2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
          这种隔离级别会产生脏读,不可重复读和幻像读。
         3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
         4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
          它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
         5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
          除了防止脏读,不可重复读外,还避免了幻像读。
  • memcached是怎么工作的?你好知道哪些缓存?
    详细转载:https://blog.csdn.net/tiedao123456789/article/details/9222475
    Memcached的工作原理
    
    Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。
    
    Memcached有两个核心组件组成:服务器端(server)和客户端(client),在一个memcached的查询中,client先通过计算key的hash值来确定kv对所处在的server位置。当server确定后,客户端就会发送一个查询请求给对应的server,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以memcached交互带给网络的影响是最小化的。
    
    举例说明:考虑以下这个场景,有三个client分别是c1,c2,c3,还有三个ms分别是s1,s2,s3:
    
    设置kv对 
    c1想设置key=”com”,value=”iQiyi”
    c1拿到server列表,并对key做hash转化,根据hash值确定kv对所存的server位置
    s2被选中了
    c1连接上s2,s2收到请求,把(key=”com”,value=”iQiyi”)存了起来
    
    获取kv对
    c3想得到key=”com”的value
    c3用相同的hash算法算出hash值,并确定key=”aa”的值存在s2上
    c3连接上s2,并从s2那边得到value=”iQiyi”
    其他任何从c1,c2,c3的想得到key=”com”的值的请求都会发向s2
  • 数据库中的锁包括什么?解释一下什么事欢乐锁?什么事悲观锁?
    数据中的锁分为两类:悲观锁和乐观锁,锁还有表级锁、行级锁 
    表级锁例如: 
    SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 
    SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 
    行级锁例如: 
    select * from table_name where id = 1 for update; 
    悲观锁(Pressimistic Locking) 
    对数据被外界(包括本系统当前的其他事务,以及来自 
    外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系 
    统不会修改数据)。例如: 
    select * from table_name where id = ‘xxx’ for update; 
    这样查询出来的这一行数据就被锁定了,在这个update事务提交之前其他外界是不能修改这条数据的,但是这种处理方式效率比较低,一般不推荐使用。 
    乐观锁(Optimistic Locking) 
    相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。 
    乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。 
    举个乐观锁的例子(数据库version默认为0): 
    不如现在一件衣服就剩一个库存了,但是有两个用户同时下单,如果这时候不加以控制很容易出现库存卖超的情况,这时候我们可以这样操作: 
    第一个用户将这件衣服读出(version=0),并将库存-1, 
    第二个用户也将这件衣服读出(version=0),并将库存-1, 
    第一个个用户完成操作,将数据库版本version+1,执行更新库存时由于提交的数据版本大于数据库记录的版本,数据被更新,数据库中的version被更新为2。 
    update goods set store=store-1,version=version+1 where id=xx and version=orginal_version 
    第二个用户也完成了操作,也将版本version+1,执行更新库存时发现执行版本和数据库记录的版本相同,不符合提交版本必须大于数据库记录版本的乐观锁策略,所以第二个用户的下单请求被驳回,我们可以通过人性化处理异常给用户提示该商品已售罄等。 
    乐观锁机制避免了长事务中的数据库加锁开销(两个用户操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。 
    悲观锁:交给数据库来处理的,由事务(分隐私和显式事务,平时单条SQL语句就是一个隐式事务)+锁 那控制的,其中事务相当于锁的作用域,根据事务的提交失败或回滚来释放掉显式事务中开启的锁。(事前处理) 
    乐观锁:是认为版本号来控制的,这种机制并发性和性能更好(事后处理)
  • mysql或Oracle查询优化,要应该注意哪些事项?
详细转载:https://blog.csdn.net/bravekingzhang/article/details/19081467

1. 为查询缓存优化你的查询
2. EXPLAIN 你的 SELECT 查询
3. 当只要一行数据时使用 LIMIT 1
4. 为搜索字段建索引
5. 在Join表的时候使用相当类型的例,并将其索引
6. 千万不要 ORDER BY RAND()
7. 避免 SELECT *
8. 永远为每张表设置一个ID
9. 使用 ENUM 而不是 VARCHAR
10. 从 PROCEDURE ANALYSE() 取得建议
11. 尽可能的使用 NOT NULL
12. Prepared Statements
13. 无缓冲的查询
14. 把IP地址存成 UNSIGNED INT
15. 固定长度的表会更快
16. 垂直分割
17. 拆分大的 DELETE 或 INSERT 语句
18. 越小的列会越快
19. 选择正确的存储引擎
20. 使用一个对象关系映射器(Object Relational Mapper)
21. 小心“永久链接”
  • 分布式session共享怎么设计?
    一、Session Replication 方式管理 (即session复制)
                简介:将一台机器上的Session数据广播复制到集群中其余机器上
                使用场景:机器较少,网络流量较小
                优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问
                缺点:广播式复制到其余机器有一定廷时,带来一定网络开销
        二、Session Sticky 方式管理
                简介:即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
               使用场景:机器数适中、对稳定性要求不是非常苛刻
               优点:实现简单、配置方便、没有额外网络开销
                缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障
        三、缓存集中式管理
               简介:将Session存入分布式缓存集群中的某台机器上,当用户访问不同节点时先从缓存中拿Session信息
               使用场景:集群中机器数多、网络环境复杂
               优点:可靠性好
               缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入

     

  • java的类加载机制的原理?
    类加载是一个将类合并到正在运行着的JVM进程中的过程。首先要加载一个类,我们必须先得将类文件加载进来并连接,并且要加上大量的验证,随后会生成一个代表着这个类的class对象,然后就可以通过它去创建新的实例了。
        这就是我所理解的Java的类加载机制。
        经过加载和连接后出来的class对象,说明这个类已经被加载到了JVM中,此后就不会再加载了。

     

  • SQL注入攻击和XSS攻击
  • MVC框架和ORM框架
  • EJB和spring
  • jboss和weblogic
  • DNS服务,CDN服务,
  • CGI
  • spring mvc中datasource配置
  • String StringBuilder StringBuffer有什么不同?

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:[转载] Spring框架——AOP前置、后置、环绕、异常通知

下一篇:[转载] MySQL的四种事务隔离级别