• Java中级知识点归纳


    (一)Java

    1.接口和抽象类的区别

    ①抽象类里可以有构造方法,而接口内不能有构造方法。

    ②抽象类中可以有普通成员变量,而接口中不能有普通成员变量。

    ③抽象类中可以包含非抽象的普通方法,而接口中所有的方法必须是抽象的,不能有非抽象的普通方法。

    ④抽象类中的抽象方法的访问类型可以是public ,protected和默认类型,但接口中的抽象方法只有public和默认类型。

    ⑤ 抽象类中可以包含静态方法,接口内不能包含静态方法。

    ⑥抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public  static类型,并且默认为public static类型。

    ⑦一个类可以实现多个接口,但只能继承一个抽象类。

    ⑧接口更多的是在系统框架设计方法发挥作用,主要定义模块之间的通信,而抽象类在代码实现方面发挥作用,可以实现代码的重用。

    2.Java虚拟机的运行时数据区有几块?线程私有和线程共享区域有哪些?

    ①程序计数器:线程私有,当前县城执行的字节码的行号指示器。

    ②虚拟机栈:线程私有,存放基本数据类型、对象引用和returnAddress类型。

    ③本地方法栈:为虚拟机使用到的Native方法服务。

    ④Java堆:线程共享,存放对象的实例,也是GC回收器管理的主要区域。

    ⑤方法区:线程共享,存放已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

    ⑥运行时常量池:方法区的一部分,存放编译期生成的各种字面量和符号引用。

    ⑦直接内存:不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,容易引起OOM异常,NIO会调用,不受Java堆大小的限制。

    3.HashMap和HashTable区别?

    ①Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
    ②Hashtable的方法是同步的,而HashMap的方法不是,因此HashTable是线程安全的,但是代码的执行效率上要慢于HashMap。
    ③HashMap允许空值和空键,但是HashTable不可以。
    ④HashMap非同步实现Map接口,是一个“链表数组”的数据结构,最大承载量是16,可以自动变长,由Entry[]控制(key,value,next),hashCode()判断key是否重复。
    ⑤建议需要做同步,使用ConcurrentHashMap,降低了锁的粒度。在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁得几率,提高并发效率。这里在并发读取时,除了key对应的value为null之外,并没有使用锁。
    4.ArrayList和LinkedList区别?
    ArrayList基于数组实现,LinkedList基于链表实现,ArrayList增加和删除比LinkedList慢,但是LinkedList在查找的时需要递归查找,效率比ArrayList慢。关于多线程方面,如果要求线程安全的,有一个Vector,不过比较多的使用的是CopyOnWriteArrayList替代ArrayList,CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主的情况。
    5.Set接口
    ①HashSet是Set接口的典型实现,HashSet按hash算法来存储元素,因此具有很好的存取和查找性能。特点:不能保证元素的排列顺序,顺序有可能发生变化;HashSet是异步的;集合元素值可以是null;当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值来确定该对象在HashSet中存储的位置。HashSet还有一个子类LinkedHashSet,其集合也是根据元素hashCode值来决定元素的存储位置,但它同时用链表来维护元素的次序,这样使得元素看起来是以插入的顺序保存的,也就是说,当遍历LinkedHashSet集合元素时,它将会按元素的添加顺序来访问集合里的元素。所以LinkedHashSet的性能略低于HashSet,但在迭代访问全部元素时将有很好的性能,因为它以链表来维护内部顺序。
    ②TreeSet是SortSet接口的唯一实现,TreeSet可以确保集合元素处于排序状态。TreeSet不是根据元素插入顺序进行排序的,而是根据元素的值来排序的。TreeSet支持两种排序方法:自然排序和定制排序。
    ③EnumSet中所有值都必须是指定枚举类型的值,它的元素也是有序的,以枚举值在枚举类的定义顺序来决定集合元素的顺序。EnumSet集合不允许加入null元素,否则会抛出NullPointerException异常。EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的static方法来创建EnumSet对象。
    ④总结:A、HashSet的性能比Treeset好,因为TreeSet需要额外的红黑树算法来维护集合元素的次序,只有当需要一个保持排序的Set时,才会用TreeSet。B、EnumSet是性能最好的,但它只能保存枚举值。
    C、它们都是线程不安全的。
    注:Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
    关于HashSet,条目数和容量之和来讲,迭代是线性的。因此,如果迭代性能很重要,那就应该慎重选择一个适当的初始容量。容量选得太大,既浪费空间,也浪费时间。默认的初试容量是101,一般来讲,它比你所需要的要多。可以使用int构造函数来指定初始容量。要分配HashSet的初始容量为17: 
                   Set s=new HashSet(17);     
    HashSet另有一个称作装载因数(load factor)的"调整参数(tuning parameter)"。
    区别:
    1. HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key。
    2.  Map的key和Set都有一个共同的特性就是集合的唯一性.TreeMap更是多了一个排序的功能.
    3.  hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可.
       a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.
       b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象 才可以真正定位到键值对应的Entry. 
       c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value 
    4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的. 
       a. Comparator可以在创建TreeMap时指定 
       b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.
    TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了. 
    6.Java中Collection和Collections的区别
    ①java.util.Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。java.util.Collections 是一个包装类。
    ②它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
    7.Java容器
    JAVA的容器---List,Map,Set 
    Collection 
    ├List 
    │├LinkedList 
    │├ArrayList 
    │└Vector 
    │ └Stack 
    └Set 
    Map 
    ├Hashtable 
    ├HashMap 
    └WeakHashMap
    !其中的Vector和Stack类现在已经极少使用。

    8.Cookie Session区别

    具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案.同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择.

    cookie机制.正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie.然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie.而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的.浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器.

    cookie的内容主要包括:名字,值,过期时间,路径和域.路径与域一起构成cookie的作用范围.若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失.这种生命期为浏览器会话期的cookie被称为会话cookie.会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的.若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间.存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口.而对于保存在内存里的cookie,不同的浏览器有不同的处理方式

    session机制.session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息.

    当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含sessionid,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存.

    保存这个sessionid的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器.一般这个cookie的名字都是类似于SEEESIONID.但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器.

    经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面.还有一种技术叫做表单隐藏字段.就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器.比如:实际上这种技术可以简单的用对action应用URL重写来代替.

    9、面向对象和面向过程的区别:

    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。  

    面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

    10、Java内存模型

    ①Java内存模型分为主内存和工作内存两个部分,其中主内存存放变量,工作内存由每个线程创建和管理,保存被该线程使用到的变量的主内存的副本拷贝。变量从主内存复制到工作内存,顺序执行read和load操作,变量从工作内存同步到主内存的时候,顺序执行store和write操作。

    对于volatile变量在各个线程的一致性:在各个线程的工作内存中,volatile存在不一致的情况,但在每次使用前都会刷新,执行引擎看不到不一致的情况,因此可以认为不存在一致性问题。

    ②原子性、可见性和有序性

    ③先行发生原则

    11、Java垃圾回收机制

    Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间。

    System.gc();

    Runtime.getRuntime().gc(); 

    上面的方法调用时用于显式通知JVM可以进行一次垃圾回收,但真正垃圾回收机制具体在什么时间点开始发生动作这同样是不可预料的,这和抢占式的线程在发生作用时的原理一样。

    12、类加载器,类加载时机

    类初始化的时机,有且仅有四个:

    A、遇到new、getstatic、putstatic、invokestatic这四条字节码指令的时候。

    B、使用java.lang.reflect进行反射调用的时候。

    C、当初始化一个类的时候,发现其父类还没有初始化,那么先去初始化它的父类。

    D、当虚拟机启动的时候,需要初始化main函数所在的类。

    13、 Java IO和NIO区别

    ①NIO操作直接缓存区,直接与OS交互,Selector IO复用机制。

    IO                NIO

    面向流            面向缓冲

    阻塞IO            非阻塞IO

    无                选择器

    Selector:Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

    ②NIO与Netty:A、NIO的类库和API复杂,使用麻烦,需要熟练使用Selector、ServerSocketChannel、SOcketChannel、ByteBuffer等。B、NIO涉及到Reactor模式,需要了解Java多线程和网络编程。C、JDKNIO Bug-epoll bug容易导致Selector空轮询,最终导致CPU100%占用,虽然JDK1.6 update18修复了这个问题,但是直到JDK1.7问题依然存在,只是降低了发生的概率。

    ③Netty的优点:A、API简单,开发门槛低;B、功能强大,预置了多种解码功能,支持多种主流协议;C、可以通过ChannelHandler对通信框架进行灵活的扩展;D、性能高,Netty的综合性能是最好的;E、Netty修复了一经发现了所有的JDKNIO BUG,成熟,稳定。

    同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

    引申:

    Java中IO的种类和应用场景:

    A、同步阻塞式:BIO。用于连接数目较小且固定的架构,对服务器资源占用高。

    B、伪异步IO变成:线程池和任务队列。

    C、NIO编程:a、缓冲徐ByteBuffer;b、通道channel全双工,同时用于读写;c、多路复用器selector。用于连接数目多且较短的架构,如聊天服务器等,但是编程复杂,存在epoll bug,导致Selector空轮询,直至CPU占用达到100%,虽然在JDK1.6 update18中有对这个bug的修复,但是在JDK1.7中依然可能会出现这个问题,只是降低了bug出现的概率。

     D、AIO编程:用于连接数目多且较长的架构,如相册服务器等,充分调用OS参与并发操作,基于JDK1.7。

    阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

    14、Java锁机制

    ①synchronized:把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有  原子性和可见性。作用:A、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。B、当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。C、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

    A、原子性:原子性意味着个时刻,只有一个线程能够执行一段代码,这段代码通过一个monitor object保护。从而防止多个线程在更新共享状态时相互冲突。

    B、可见性:可见性则更为微妙,它要对付内存缓存和编译器优化的各种反常行为。它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。

    C、volatile只保证可见性和禁止重排序,不保证原子性。

    ②synchronized限制:

    A.它无法中断一个正在等候获得锁的线程;

    B.也无法通过投票得到锁,如果不想等下去,也就没法得到锁;

    C.同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。

    ③java.util.concurrent.lock:

    ReentrantLock 类实现了Lock,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。

    用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内。

    ④ReentrantWriteReadLock中的ReadLock和WWriteLock,在全为读时实现并发读,并发读写或并发写时候加锁。

    总结:synchronized是Java原语,阻塞的,竞争锁机制;新锁更加面向对象,并且支持中断和支持公平锁。

    15、Java基本数据类型

    boolean(1)、byte(8)、char(16)、short(16)、int(32)、float(32)、long(64)、double(64)

    16、Java内存模型

    ①特点:原子性、可见性、有序性。

    A、原子性:read、load、use、store、write,synchronized关键字保证原子性

    B、可见性:synchronized、volatile、final保证可见性

    C、有序性:synchronized保证有序性

    17、设计模式

    ①分类:

    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    其实还有两类:并发型模式和线程池模式。

    ②设计模式6大原则:

    A、开闭原则(Open Close Principle)

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    B、里氏代换原则(Liskov Substitution Principle)

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

    C、依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

    D、接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    F、迪米特法则(最少知道原则)(Demeter Principle)

    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

    F、合成复用原则(Composite Reuse Principle)

    原则是尽量使用合成/聚合的方式,而不是使用继承。

    18、Java反射

    反射机制指的是程序在运行时能够获取自身的信息。

    为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,

       静态编译:在编译时确定类型,绑定对象,即通过。

        动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

        一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中

    它的灵活性就表现的十分明显。

    作用:①首先得根据传入的类的全名来创建Class对象。 ②获得类方法的方法。③  获得类中属性的方法。

    缺点:①性能第一:反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。②安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet。③内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

    19、Java引用

    ①假设我们在函数中写了如下这个简单的语句:

    StringBuffer str= new StringBuffer("Hello world"); 

    别看这个语句简单,其实包含了如下三个步骤:

    首先,new StringBuffer("Hello world")在堆里申请了一坨内存,把创建好的StringBuffer对象放进去。其次,StringBuffer str声明了一个指针。这个指针本身是存储在栈上的(因为语句写在函数中),可以用来指向某个StringBuffer类型的对象。或者换一种说法,这个指针可以用来保存某个StringBuffer对象的地址。最后,当中这个等于号(赋值符号)把两者关联起来,也就是把刚申请的那一坨内存的地址保存成str的值,完成引用。

    ②final常量的问题

    针对引用类型变量的final修饰符也是很多人搞混淆的地方。实际上final只是修饰指针的值(也就是限定指针保存的地址不能变)。至于该指针指向的对象,内容是否能变,那就管不着了。所以,对于如下语句:

    final StringBuffer strConst = new StringBuffer();

    你可以修改它指向的对象的内容,比如:

    strConst.append(" ");

    但是不能修改它的值,比如:

    strConst = null;

    ③传参的问题:

    例如:System.out.println(str);这个语句又是什么意思捏?这时候就两说了。

    第一种理解:可以认为传进函数的是str这个指针,指针说白了就是一个地址的值,说得再白一点,就是个整数。按照这种理解,就是传值的方式。也就是说,参数传递的是指针本身,所以是传值的。

    第二种理解:可以认为传进去的是StringBuffer对象,按照这种理解,就是传引用方式了。因为我们确实是把对象的地址(也就是引用)给传了进去。

    20、 线程、线程池:

    ①创建线程有两种方式:继承Thread或实现Runnable。Thread实现了Runnable接口,提供了一个空的run()方法,所以不论是继承Thread还是实现Runnable,都要有自己的run()方法。一个线程创建后就存在,调用start()方法就开始运行(执行run()方法),调用wait进入等待或调用sleep进入休眠期,顺利运行完毕或休眠被中断或运行过程中出现异常而退出。

    ②wait和sleep比较:sleep方法有:sleep(long millis),sleep(long millis, long nanos),调用sleep方法后,当前线程进入休眠期,暂停执行,但该线程继续拥有监视资源的所有权。到达休眠时间后线程将继续执行,直到完成。若在休眠期另一线程中断该线程,则该线程退出。等待有其它的线程调用notify()或notifyAll()进入调度状态,与其它线程共同争夺监视。

    ③线程池:多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。一个线程池包括以下四个基本组成部分:

    A、线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;

    B、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;

    C、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;

    D、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

    ④线程池分类:

    A、newFixedThreadPool  创建一个指定工作线程数量的线程池。

    每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

    B、newCachedThreadPool创建一个可缓存的线程池。

    这种类型的线程池特点是:

    1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger.MAX_VALUE), 这样可灵活的往线程池中添加线程。

    2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

    C、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。

    单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

    D、newScheduleThreadPool  创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。

    ⑤Executors类,提供了一系列静态工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

    ⑥线程池参数:

    A、corePoolSize(线程池的基本大小)

    B、runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。

    1)LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

    2)SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

    3)PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

    C、maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。

    D、ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。

    E、RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略:

    1)AbortPolicy:直接抛出异常。

    2)CallerRunsPolicy:只用调用者所在线程来运行任务。

    3)DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

    4)DiscardPolicy:不处理,丢弃掉。

    5)当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

    F、keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。

    G、TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

    21、J2EE的13种规范

    (1)、JDBC(java Database Connectivity):
    JDBC API为访问不同的数据库提供了一种统一的途径,就像ODBC一样,JDBC对开发者屏蔽了一些细节问题,同时,JDBC对数据库的访问也具有平台无关性。

    (2)、JNDI(Java Name and Directory Interface):
    JNDI API 被用于执行名字和目录服务。它提供了一致的模型用来存取和操作企业级的资源如DNS和LDAP,本地文件系统,或应用服务器中的对象。

    (3)、EJB(Enterprise JavaBean):
    J2ee技术之所以赢得全体广泛重视的原因之一就是EJB,他们提供了一个框架开发和实施分布式商务逻辑,由此很显著简化了具有可伸缩性和高度复杂的企业级应用开发。EJB规范定义了EJB组件何时如何与他们的容器继续拧交互作用。容器负责提供公用的服务,例如目录服务、事务管理、安全性、资源缓冲池以及容错性。但是注意的是,EJB并不是J2EE的唯一途径。正是由于EJB的开放性,使得有的厂商能够以一种和EJB平行的方式来达到同样的目的。

    (4)、RMI(RemoteMethod Invoke):remote(遥远的) invoke(调用):
    正如其名字所表示的那样,RMI协议调用远程对象上方法。它使用了序列化方式在客户端和服务器端传递数据。RMI是一种被EJB使用的更底层的协议。

    (5)、Java IDL(接口定义语言)/CORBA:公共对象请求代理结构(Common Object Request Breaker Architecture):
    在java IDL的支持下,开发人员可以将Java和CORBA集成在一起。他们可以创建Java对象并使之可以在CORBA ORB中展开,或者他们还可以创建Java类并做为和其他ORB一起展开的CORBA对象客户。后一种方法提供了另外一种途径,通过它可以被用于你的新的应用和旧系统相集成。

    (6)、JSP(Java Server Pages):
    Jsp页面由html代码和嵌入其中的Java新代码所组成。服务器在页面被客户端所请求以后对这些java代码进行处理,然后将生成的html页面返回给客户端的浏览器。

    (7)、Java Servlet:
    servlet是一种小型的java程序,它扩展了web服务器的功能。作为一种服务器端的应用,当被请求时开始执行,这和CGI Perl脚本很相似。Servlet提供的功能大多和jsp类似,不过实现方式不同。JSP通过大多数的html代码中嵌入少量的java代码,而servlet全部由java写成并生成相应的html。

    (8)、XML(Extensible Markup Language):
    XML是一种可以用来定义其他标记语言的语言。它被用来在不同的商务过程中共享数据。XML的发展和Java是互相独立的,但是,它和java具有相同目标正是平台独立。通过java和xml的组合,我们可以得到一个完美的具有平台独立性的解决方案。

    (9)、JMS(Java Message Service):
    Ms是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域,有支持发布/订阅类型的域,并且提供对下列类型的支持:经认可的消息传递,事务性消息传递,一致性消息和具有持久性的订阅者的支持。JMS还提供了另一种方式对您的应用与旧的后台系统相集成。

    (10)、JTA(Java Transaction Architecture):
    JTA定义了一种标准API,应用系统由此可以访问各种事务监控。

    (11)、JTS(Java  Transaction Service):
    JTS是CORBA OTS事务监控的基本实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API(JTA)规范,并且在较底层实现OMG OTS specification 的java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。

    (12)、JavaMail:
    JavaMail是用于存取邮件服务的API,它提供了一套邮件服务器的抽象类。不仅支持SMTP服务器,也支持IMAP服务器。

    (13)、JAF(JavaBeans Activation Framework):
    JavaMail利用JAF来处理MIME编码的邮件附件。MIME的字节流可以被转换成java对象,或者转换自Java对象。大多数应用都可以不需要直接使用JAF。

    (二)服务器

    1、web服务器nginx和apache的对比分析

    ①nginx相对于apache的优点:

    轻量级,同样起web 服务,比apache 占用更少的内存及资源 ,抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能,高度模块化的设计,编写模块相对简单。

    apache相对于nginx 的优点:A.rewrite ,比nginx 的rewrite 强大;B.动态页面,模块超多,基本想到的都可以找到;C.少bug ,nginx 的bug 相对较多;D.超稳定.

    一般来说,需要性能的web 服务,用nginx 。如果不需要性能只求稳定,那就apache.

    ②作为 Web 服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率。Nginx采用C进行编写, 不论是系统资源开销还是CPU使用效率都比 Perlbal 要好很多.

    ③Nginx 配置简洁,Apache 复杂。Nginx 静态处理性能比 Apache 高 3倍以上,Apache 对 PHP 支持比较简单,Nginx 需要配合其他后端用。Apache 的组件比 Nginx 多,现在 Nginx 才是Web 服务器的首选。

    ④最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程。

    ⑤nginx处理静态文件好,耗费内存少.但无疑apache仍然是目前的主流,有很多丰富的特性.所以还需要搭配着来.当然如果能确定nginx就适合需求,那么使用nginx会是更经济的方式。

    ⑥nginx处理动态请求是鸡肋,一般动态请求要apache去做,nginx只适合静态和反向。

    ⑦Nginx优于apache的主要两点:A.Nginx本身就是一个反向代理服务器 B.Nginx支持7层负载均衡;其他的当然,Nginx可能会比 apache支持更高的并发。

    (三)数据库

    1、数据库优化:

    ①方法:MySQL可以建分表,读写分离,建索引,一般经常更新的字段不适合建索引,建索引会降低数据非查询操作的效率。主键是一种特殊的索引。

    ②导致索引失效的情况:

    A、如果条件中有or,即使其中有条件带索引也不会使用到。

    B、对于多列索引,不是使用的第一部分,则不会使用索引。

    C、like查询是以%开头,而不是以%结尾的。

    D、如果索引列类型是字符串,一定要在条件中将数据使用引号引用起来,否则不使用索引。

    E、如果mysql估计使用全表扫描要比使用索引快,则不使用索引。

    2、MySQL引擎的种类和区别

    ①种类:MyISAM、InnoDB、MEMORY、MERGE、Archive、Blackhole、CSV、Federate、Merge、NDB集群引擎,第三方引擎:OLTP类引擎、面向列的存储引擎、社区存储引擎。

    ②区别:

    A、MyISAM是MySQL5.1及之前的默认存储引擎。MyISAM不支持事务、也不支持外键,但其访问速度快,对事务完整性没有要求。MyISAM表还支持3中不同的存储格式:

    1 静态表

    2 动态表

    3 压缩表

    B、InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是比起MyISAM存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。 InnoDB存储方式为两种:1 使用共享表空间存储 2 使用多表空间

    C、MEMORY存储引擎使用存在内存中的内容来创建表。每个MEMORY表只实际对应一个磁盘文件。MEMORY类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH索引。但是一旦服务关闭,表中的数据就会丢失掉。

    D、MERGE存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同。MERGE表本身没有数据,对MERGE类型的表进行查询、更新、删除的操作,就是对内部的MyISAM表进行的。

    3、数据库事务

    (1)四个特性:ACID,原子性,一致性,隔离性,持久性。

    (2)四个隔离级别:

    √: 可能出现    ×: 不会出现

      脏读 不可重复读 幻读
    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(可串行化)
           这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

    这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
    i.   脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
    ii.  不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
    iii. 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

    (3)一致性处理: 

    A、开启事务。B、申请写权限,也就是给对象(表或记录)加锁。C、假如失败,则结束事务,过一会重试。D、假如成功,也就是给对象加锁成功,防止其他用户再用同样的方式打开。E、进行编辑操作。F、写入所进行的编辑结果。G、假如写入成功,则提交事务,完成操作。 H、假如写入失败,则回滚事务,取消提交。I、(G、H)两步操作已释放了锁定的对象,恢复到操作前的状态。

    (4)基于事务的数据库引擎的选型:如果考虑到事务,推荐选用MySQL INNODB引擎;如果考虑速度,建议考虑MySQL MyISAM引擎,并且需要在代码层面做比较复杂的处理,如通过多线程、异步、非阻塞等方式对数据库进行清理,同时需要信号量、栏栅、数据库标志位等工具保证数据一致性。

    4、海量数据处理

    (1)数据库扩展:

    ①纵向扩展:基于业务的高度隔离性和数据的安全性,对业务和数据进行合理的切分,进行主-备机分离,主-主同步,主-从同步(对于MySQL数据库是单向异步同步机制),主-从热备等操作。

    ②横向扩展:对数据表进行横向切分,按一定的规则(hash取模分、user_id尾数、自定义算法等)对数据表进行横向切分。

    关于数据库架构和扩展方面的文章请见:Mysql在大型网站的应用架构演变

    (2)分布式数据方案:

    ①提供分库规则和路由规则(RouteRule简称RR),将上面的说明中提到的三中切分规则直接内嵌入本系统,具体的嵌入方式在接下来的内容中进行详细的说明和论述; 

    ②引入集群(Group)的概念,保证数据的高可用性; 

    ③引入负载均衡策略(LoadBalancePolicy简称LB); 

    ④引入集群节点可用性探测机制,对单点机器的可用性进行定时的侦测,以保证LB策略的正确实施,以确保系统的高度稳定性; 

    ⑤引入读/写分离,提高数据的查询速度。

    具体描述请参见博文:MySQL 海量数据的存储和访问解决方案

    (3)数据库切分策略介绍,请见博文:数据库Sharding的基本思想和切分策略

    (4)随着数据量随着业务的发展不断增大,传统的关系型数据库RDB已经无法满足需要,这时需要引入新的海量数据处理解决方案:Apache HBase。

    (四)框架

    1、Struts2

    ①工作原理

    A、在Struts2框架中的处理大概分为以下几个步骤:

    1)客户端初始化一个指向Servlet容器(例如Tomcat)的请求

    2)这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)

    3)接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action

    4)如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy

    5)ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类

    6)ActionProxy创建一个ActionInvocation的实例。

    7)ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。

    8)一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。

    ②工作流程:

    1)客户端在浏览器中输入一个url地址。

    2)这个url请求通过http协议发送给tomcat。

    3)tomcat根据url找到对应项目里面的web.xml文件。

    4)在web.xml里面会发现有struts2的配置。

    5)然后会找到struts2对应的struts.xml配置文件。

    6)根据url解析struts.xml配置文件就会找到对应的class。

    7)调用完class返回一个字String,根据struts.xml返回到对应的jsp。

    2、Spring原理

    ①IoC(Inversion of control): 控制反转,依赖注入

    1)IoC:

    概念:控制权由对象本身转向容器;由容器根据配置文件去创建实例并创建各个实例之间的依赖关系

    2)依赖IoC容器负责管理bean,有两种,一种是BeanFactory,另一种是ApplicationContext,但是ApplicationContext继承与BeanFactory。

    核心:bean工厂;在Spring中,bean工厂创建的各个实例称作bean

    ②AOP(Aspect-Oriented Programming): 面向方面编程

    1)代理的两种方式:

    静态代理:

     针对每个具体类分别编写代理类;

     针对一个接口编写一个代理类;

    动态代理:

    针对一个方面编写一个InvocationHandler,然后借用JDK反射包中的Proxy类为各种接口动态生成相应的代理类

    2) AOP的主要原理:动态代理

    实现:有两种:JDK Proxy和Cglib,Spring规定对于有接口的类用JDK Proxy,对于无接口和抽象类用Cglib,虽然Cglib均可以代理,但是Cglib复杂,效率低。但是Cglib有例外,就是代理的类中不能是final修饰的类或者类中有final方法。

    3、Spring、Struts2、Servlet对比

    ①Servlet原理:Tomcat 的容器等级中,Context 容器是直接管理 Servlet 在容器中的包装类 Wrapper,所以 Context 容器如何运行将直接影响 Servlet 的工作方式。

    A、Servlet生命周期详解

    Servlet的生命周期可以分为四个阶段,即装载类及创建实例阶段、初始化阶段、服务阶段和实例销毁阶段。下面针对每个阶段的编程任务及注意事项进行详细的说明。

    B、Servlet创建过程

    在默认情况下Servlet实例是在第一个请求到来的时候创建,以后复用。一旦Servlet实例被创建,Web服务器会自动调用init(ServletConfig config)方法来初始化该Servlet。其中方法参数config中包含了Servlet的配置信息,比如初始化参数,该对象由服务器创建。init方法在Servlet生命周期中只执行一次,而且该方法执行在单线程的环境下,因此开发者不用考虑线程安全的问题。

     

    C、服务

    一旦Servlet实例成功创建及初始化,该Servlet实例就可以被服务器用来服务于客户端的请求并生成响应。在服务阶段Web服务器会调用该实例的service(ServletRequest request,ServletResponse response)方法,request对象和response对象有服务器创建并传给Servlet实例。request对象封装了客户端发往服务器端的信息,response对象封装了服务器发往客户端的信息。

    为了提高效率,Servlet规范要求一个Servlet实例必须能够同时服务于多个客户端请求,即service()方法运行在多线程的环境下,Servlet开发者必须保证该方法的线程安全性。

    Serlvet接口只定义了一个服务方法就是service,而HttpServlet类实现了该方法并且要求调用下列的方法之一:

    doGet:处理GET请求

    doPost:处理POST请求

    当发出客户端请求的时候,调用service 方法并传递一个请求和响应对象。Servlet首先判断该请求是GET 操作还是POST 操作。然后它调用下面的一个方法:doGet 或 doPost。如果请求是GET就调用doGet方法,如果请求是POST就调用doPost方法。

    ②对比:Spring主要有IoC和AOP,Spring中IoC管理的bean为单例模式的,可以配置成原型模式。如果用Spring管理struts2的bean,必须要设置成原型模式,因为struts2封装来了servlet,隔离了servlet的特性,Action不同于Spring,已经是原型模式了。

    4、memcached和redis区别

    ①Memcached出现于2003年,key支持250bytes,value支持1MB;redis出现于2009年,key和value都是支持512MB的。

    持久化方面,memcached过期失效,redis可以缓存回收(6种回收机制),有存储,可算为NOSQL,有优化,支持190多种命令。

    集群方面,memcached不支持集群,基于两次哈希,第一次哈希找到服务器节点,第二次哈希找到存储的值。

    1)Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。

    2)Redis支持数据的备份,即master-slave模式的数据备份。

    3)Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

    Redis只会缓存所有的key的信息,如果Redis发现内存的使用量超过了某一个阀值,将触发swap的操作,Redis根据“swappability= age*log(size_in_memory)”计算出哪些key对应的value需要swap到磁盘。然后再将这些key对应的value持久化到磁盘中,同时在内存中清除。这种特性使得Redis可以保持超过其机器本身内存大小的数据。当然,机器本身的内存必须要能够保持所有的key,毕竟这些数据是不会进行swap操作的。

    ②memcached、redis和mongoDB三者之间的对比:

    1)性能

    都比较高,性能对我们来说应该都不是瓶颈

    总体来讲,TPS(每秒事务处理量)方面redis和memcache差不多,要大于mongodb

    2)操作的便利性

    memcache数据结构单一

    redis丰富一些,数据操作方面,redis更好一些,较少的网络IO次数

    mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富

    3)内存空间的大小和数据量的大小

    redis在2.0版本后增加了自己的VM特性,突破物理内存的限制;可以对key value设置过期时间(类似memcache)

    memcache可以修改最大可用内存,采用LRU算法

    mongoDB适合大数据量的存储,依赖操作系统VM做内存管理,吃内存也比较厉害,服务不要和别的服务在一起

    4)可用性(单点问题)

    对于单点问题,

    redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制,因性能和效率问题,

    所以单点问题比较复杂;不支持自动sharding,需要依赖程序设定一致hash 机制。

    一种替代方案是,不用redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复制的方式(需要自己实现),一致性问题和性能的权衡

    Memcache本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的hash或者环状的算法,解决单点故障引起的抖动问题。

    mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。

    5)可靠性(持久化)

    对于数据持久化和数据恢复,

    redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响;memcache不支持,通常用在做缓存,提升性能;MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性。

    6)数据一致性(事务支持)

    Memcache 在并发场景下,用cas保证一致性。

    redis事务支持比较弱,只能保证事务中的每个操作连续执行。

    mongoDB不支持事务

    7)数据分析

    mongoDB内置了数据分析的功能(mapreduce),其他不支持。

    8)应用场景

    redis:数据量较小的更性能操作和运算上。

    memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用sharding)。

    MongoDB:主要解决海量数据的访问效率问题。

    5、设计一个缓存

    A、过期时间

    B、多态实现访问量

    C、弱引用、软引用。

    D、WeakHashMap的使用。

    缓存技术的实现原理:LRU(Least Recently Used)缓存技术

    可以使用两个标准的数据结构来实现,Map和Queue。因为需要支持多线程,需要使用实现了java.util.concurrent.*的Map和Queue。主要思路是使用一个Queue来维护FIFO和Map来对数据进行排序,当向缓存添加新的元素时,共有以下三种可能:

    ①如果该元素已经在Cache中存在(Map),我们会从Queue中删除元素并将其添加到Queue的第一个位置;

    ②如果缓存已经无法满足新增新的元素,我们会从Queue和Map中删除最后面的那个元素并把新元素添加进来;

    ③同时在Map和Queue中增加新的元素。

     参考代码:

    复制代码
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentLinkedQueue;
    
    public class LRUCache<K, V> {
    
        // LRU 缓存的最大容量.
        private final int capacity;
        // 用来保持最近使用的元素的 Queue.
        private ConcurrentLinkedQueue<K> queue;
    
        private ConcurrentHashMap<K, V> map;
    
        /**
         * 初始化 LRU 缓存
         * 
         * @param capacity
         */
        public LRUCache(final int capacity) {
            this.capacity = capacity;
            this.queue = new ConcurrentLinkedQueue<K>();
            this.map = new ConcurrentHashMap<K, V>(capacity);
        }
    
        /**
         * 检查该元素释放在缓存中存在,如果不存在则返回 null
         * 
         * @param key
         * @return
         */
        public V get(final K key) {
            return map.get(key);
        }
    
        /**
         * 将元素添加到 LRU 缓存。如果 Key 已存在,则将其放到缓存的第一位置
         * 
         * @param key
         * @param value
         * @throws NullPointerException
         */
        public synchronized void put(final K key, final V value) {
            if (key == null || value == null) {
                throw new NullPointerException();
            }
            if (map.containsKey(key)) {
                queue.remove(key);
            }
            while (queue.size() >= capacity) {
                K expiredKey = queue.poll();
                if (expiredKey != null) {
                    map.remove(expiredKey);
                }
            }
            queue.add(key);
            map.put(key, value);
        }
    }
    复制代码
     

    6、RPC

    7、序列化

    8、新技术

    (1)Netty:

    Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于NIO的客户,服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。 “快速”和“简单”并不意味着会让你的最终应用产生维护性或性能上的问题。Netty 是一个吸收了多种协议的实现经验,这些协议包括FTP,SMTP,HTTP,各种二进制,文本协议,并经过相当精心设计的项目,最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。

    (2)MongoDB:

    Mongo DB 是目前在IT行业非常流行的一种非关系型数据库(NoSql),其灵活的数据存储方式备受当前IT从业人员的青睐。Mongo DB很好的实现了面向对象的思想(OO思想),在Mongo DB中 每一条记录都是一个Document对象。Mongo DB最大的优势在于所有的数据持久操作都无需开发人员手动编写SQL语句,直接调用方法就可以轻松的实现CRUD操作。

    (3)Hadoop:

    Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。 Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。

    (4)Solr:

    Solr是一个高性能,采用Java5开发,SolrSolr基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。

    (5)ActiveMQ:

    ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

    (6)Velocity:Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅使用简单的模板语言(template language)来引用由java代码定义的对象。

    (7)Openfire :Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。

    (8)Node.js:

    Node.js是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行的数据密集型的实时应用。 V8引擎执行Javascript的速度非常快,性能非常好。 Node是一个Javascript运行环境(runtime)。实际上它是对Google V8引擎进行了封装。V8引 擎执行Javascript的速度非常快,性能非常好。Node对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。

    (9)Ruby on Rails:

    Ruby on Rails 是一个可以使你开发,部署,维护 web 应用程序变得简单的框架。ruby on rails使用的实时映射技术和元编程技术,免去了开发人员在开发过程中编写大量样板文件代码的烦恼。在少数需要使用样板文件代码的时候,开发人员可以通过ruby on rails内建的生成器脚本实时创建,而不再是通过手工编写。rails的这个特点可以使开发人员更专注于系统的逻辑结构,而不必为一些琐碎的细节所烦扰。

    (10)Docker:

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架或包括系统。

    (11)ElasticSearch:

    ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是第二最流行的企业搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    (12)RESTful WebService:

    RESTful WebService是比基于SOAP消息的WebService简单的多的一种轻量级Web服务,RESTful WebService是没有状态的,发布和调用都非常的轻松容易。

    (13)Hessian:

    Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。

    (14)WebLogic:

    WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。

    (15)OSGi:

    OSGi(Open Service Gateway Initiative)技术是面向Java的动态模型系统。OSGi服务平台向Java提供服务,这些服务使Java成为软件集成和软件开发的首选环境。Java提供在多个平台支持产品的可移植性。OSGi技术提供允许应用程序使用精炼、可重用和可协作的组件构建的标准化原语。这些组件能够组装进一个应用和部署中。

     

  • 相关阅读:
    BZOJ1058:[ZJOI2007]报表统计(Splay,堆)
    BZOJ3224:普通平衡树(Splay)
    BZOJ3781:小B的询问(莫队)
    21. [HAOI2005] 希望小学 (wa1)
    cogs 2509. 森林大礼包
    libreoj #119. 最短路
    libreoj #514. 「LibreOJ β Round #2」模拟只会猜题意
    cogs 1647. 油田[uva572]
    css的部分应用示例
    html之表格
  • 原文地址:https://www.cnblogs.com/QuickSlow/p/12643442.html
Copyright © 2020-2023  润新知