Java基础知识整理

2019-10-16  本文已影响0人  孟婆灬来碗汤

一、String和Stringbuffer和StringBuilder的区别?

String:字符串常量

StringBuffer:字符串变量;线程安全的

StringBuilder:字符串变量;线程非安全的

三者执行速度比较:StringBuilder > StringBuffer > String

1.String类的内容一旦声明后是不可改变的,改变的只是其内存的指向,而StringBuffer类的对象内容是可以改变的。

2.对于StringBuffer,不能像String那样直接通过赋值的方式完成对象实例化,必须通过构造方法的方式完成。

3.StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于字符串类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入,删除等操作,使用StringBuffer要更加适合一些。

4.StringBuilder,StringBuffer 之间的最大不同在于 StringBuilder 的方法是线程非安全的(不能同步访问)。

5.StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类,然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

StringBuilder与StringBuffer二者的区别主要是在运行速度和线程安全这两方面。

1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的

2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低。

三者使用的总结:1.如果要操作少量的数据用 = String

2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer


二、ArrayList,Vector, LinkedList的存储性能和特性

  ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized思路方法(线程安全) ,通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项前后项即可,所以插入速度较快


三、HashMap和Hashtable的区别

HashMap:①线程非安全的 

                   ②性能方面:由于线程是非安全的,所以每个方法不需要阻塞其他线程,所以性能要优于HashTable

                   ③空值问题:允许键值为Null

                   ④实现方式:继承AbstractMap类

                   ⑤扩容:源码中初始值 1 << 4 ,初始值为16,负载因子默认为0.75(详细请看该链接下《说说hashMap 使用的是哪种数据结构》链接内容

                   ⑥迭代器:HashMap 中的 Iterator 迭代器是 fail-fast 的,当其他线程改变了HashMap 的结构,如:增加、删除元素,将会抛出 ConcurrentModificationException 异常,而 Hashtable 则不会。

HashTable:①线程安全的,线程安全的原因是所有的元素操作都是用synchronized修饰的,而HashMap没有

                     ②性能方面:由于是线程安全的,所以每个方法需要阻塞其他线程,所以性能要弱于HashMap

                     ③空值问题:不允许键值为null

                     ④实现方式:继承Dictionary类

                     ⑤扩容:源码中初始值为11,负载因子默认为0.75

                     ⑥迭代器: Hashtable 的 Enumerator 不是 fail-fast 的,Enumeration是个接口,不是类,再次,这个东西就是为了实现遍历的,现在已经被迭代器Iterator取代了,Enumeration和iterator最主要区别,其实就是Iterator可以删除元素,但是Enumration却不能。

最大的区别是,Hashtable的思路方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的思路方法实现同步,而HashMap 就必须为的提供外同步。


四、 final, finally, finalize的区别

final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。

finally:是异常处理语句结构的一部分,表示总是执行。 

finalize:是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。


五、swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

swtich中只能用byte short int char


六、线程的几种状态

创建:当new了一个线程,并没有调用start之前,线程处于创建状态;

就绪:当调用了start之后,线程处于就绪状态,这是,线程调度程序还没有设置执行当前线程;

运行:线程调度程序执行到线程时,当前线程从就绪状态转成运行状态,开始执行run方法里边的代码;

阻塞:线程在运行的时候,被暂停执行(通常等待某项资源就绪后在执行,sleep、wait可以导致线程阻塞),这是该线程处于阻塞状态;

死亡:当一个线程执行完run方法里边的代码或调用了stop方法后,该线程结束运行

生命周期如下图:


七、多线程有几种实现方式?

多线程的几种实现方式:①继承Thread类,重写Run()

                                        ②实现Runnable接口,创建步骤如下:

                                                   定义一个类实现Runnable接口,作为线程任务类

                                                    重写run方法,并实现方法体,方法体的代码就是线程所执行的代码

                                                    定义一个可以运行的类,并在main方法中创建线程任务类

                                                    创建Thread类,并将线程任务类做为Thread类的构造方法传入

                                                    启动线程

                                        ③使用内部类的方式:将Thread和Runnable作为内部子类实现

                                        ④定时器:定时器Timer(JDK提供API)是一个基于线程的工具类,可以定时的来执行某个任务。                                                                                               提供定时任务的三方框架:Spring的Schedule和Quartz框架

                                             ⑤带返回值的线程实现方式:

                                                  1)实现Callable接口,实现call(),这个接口类似于Runnable接口,但比Runnable接口更加强大,                                                                                    增加了异常和返回值。 

                                                  2)创建一个FutureTask,指定Callable对象,做为线程任务。

                                                  3)创建线程,指定线程任务。

                                                  4)启动线程

                                              ⑥基于线程池的方式:

                                                  概念:一些线程的集合称为线程池,线程池可以很好的提高性能,线程池在系统启动时创建大量的空闲线程,程序将任务提交以后,线程就会启动一个线程执行该任务,任务结束以后,线程并不会死亡,而是返回线程池,成为空闲状态,当代执行下一个任务。

                                                   工作机制:①在线程池的编程模式下,任务是提交给整个线程池的,而不是直接提交给某一个线程,线程池在获取到任务后,会寻找是否有空闲的线程,如果有则将任务提交给这个线程

                                                                     ②一个线程只能执行一个任务,但可以同时向一个线程池提交多个任务

                                                     使用线程池的原因:多线程运行时,系统会不断的启动和关闭新线程,成本高,会过度消耗资源,以及过度的切换线程,会造成系统资源的崩溃,所以,线程池是最优选择

四种常见的线程池:

①线程池的返回值都是ExecutorService,ExecutorService是Java提供的用于管理线程池的类。该类作用是:控制线程数量和重用线程

②四种线程池介绍:

1)Executors.newCacheThreadPool():可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务

2)Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。

3)Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行

4)Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。


八、线程池的参数总结:

①corePoolSize:核心线程数

        * 核心线程会一直存活,及时没有任务需要执行

         * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

         * 设置  allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

②queueCapacity:任务队列容量(阻塞队列)

         * 当核心线程数达到最大时,新任务会放在队列中排队等待执行

③maxPoolSize:最大线程数

         * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务

         * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

④keepAliveTime:线程空闲时间

           * 当线程空闲时间达到  keepAliveTime时,线程会退出,直到线程数量=corePoolSize

           * 如果 allowCoreThreadTimeout=true,则会直到线程数量=0

⑤allowCoreThreadTimeout:允许核心线程超时

⑥rejectedExecutionHandler:任务拒绝处理器

            * 两种情况会拒绝处理任务:

            - 当线程数已经达到 maxPoolSize ,切队列已满,会拒绝新任务

            - 当线程池被调用 shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用 shutdown()和线程池真正 shutdown之间提交任务,会拒绝新任务

        * 线程池会调用 rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常

        * ThreadPoolExecutor类有几个内部实现类来处理这类情况:

            - AbortPolicy 丢弃任务,抛运行时异常

            - CallerRunsPolicy 执行任务

            - DiscardPolicy 忽视,什么都不会发生

            - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

        * 实现 RejectedExecutionHandler 接口,可自定义处理器


基于JDK1.8的ConcurrentHashMap的源码分析:

ConcurrentHashMap是线程安全的,利用的是 CAS算法和Synchronized 来保证并发更新的安全

数据机构是:数组+链表+红黑树

重要的成员变量:

    table:默认为null,初始化发生在第一次插入操作,默认大小为16的数组,用来存储Node节点数据,扩容时大小总是2的幂次方。

    nextTable:默认为null,扩容时新生成的数组,其大小为原数组的两倍。

    sizeCtl :默认为0,用来控制table的初始化和扩容操作,具体应用在后续会体现出来。

           ① -1 代表table正在初始化

            ②-N 表示有N-1个线程正在进行扩容操作

            ③其余情况:

                    1、如果table未初始化,表示table需要初始化的大小。

                     2、如果table初始化完成,表示table的容量,默认是table大小的0.75倍,居然用这个公式算0.75(n - (n >>> 2))。

```

public classConcurrentHashMapextendsAbstractMapimplementsConcurrentMap, Serializable {private static final longserialVersionUID = 7249069246763182397L;// 表的最大容量private static final intMAXIMUM_CAPACITY = 1 << 30;// 默认表的大小private static final intDEFAULT_CAPACITY = 16;// 最大数组大小static final intMAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 默认并发数private static final intDEFAULT_CONCURRENCY_LEVEL = 16;// 装载因子private static final floatLOAD_FACTOR = 0.75f;// 转化为红黑树的阈值static final intTREEIFY_THRESHOLD = 8;// 由红黑树转化为链表的阈值static final intUNTREEIFY_THRESHOLD = 6;// 转化为红黑树的表的最小容量static final intMIN_TREEIFY_CAPACITY = 64;// 每次进行转移的最小值private static final intMIN_TRANSFER_STRIDE = 16;// 生成sizeCtl所使用的bit位数private static intRESIZE_STAMP_BITS = 16;// 进行扩容所允许的最大线程数private static final intMAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;// 记录sizeCtl中的大小所需要进行的偏移位数private static final intRESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;// 一系列的标识static final intMOVED = -1;// hash for forwarding nodesstatic final intTREEBIN = -2;// hash for roots of treesstatic final intRESERVED = -3;// hash for transient reservationsstatic final intHASH_BITS = 0x7fffffff;// usable bits of normal node hash//

    /** Number of CPUS, to place bounds on some sizings */

    // 获取可用的CPU个数static final intNCPU = Runtime.getRuntime().availableProcessors();//

    /** For serialization compatibility. */

    // 进行序列化的属性private static finalObjectStreamField[] serialPersistentFields = {newObjectStreamField("segments", Segment[].class),newObjectStreamField("segmentMask", Integer.TYPE),newObjectStreamField("segmentShift", Integer.TYPE)    };// 表transient volatileNode[] table; // 下一个表private transient volatileNode[] nextTable; //

    /**

    * Base counter value, used mainly when there is no contention,

    * but also as a fallback during table initialization

    * races. Updated via CAS.

    */

    // 基本计数private transient volatile longbaseCount; //

    /**

    * Table initialization and resizing control.  When negative, the

    * table is being initialized or resized: -1 for initialization,

    * else -(1 + the number of active resizing threads).  Otherwise,

    * when table is null, holds the initial table size to use upon

    * creation, or 0 for default. After initialization, holds the

    * next element count value upon which to resize the table.

    */

    // 对表初始化和扩容控制private transient volatile intsizeCtl;/**

    * The next table index (plus one) to split while resizing.

    */

    // 扩容下另一个表的索引private transient volatile inttransferIndex;/**

    * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.

    */

    // 旋转锁private transient volatile intcellsBusy;/**

    * Table of counter cells. When non-null, size is a power of 2.

    */

    // counterCell表private transientvolatileCounterCell[] counterCells;// views

    // 视图private transientKeySetView keySet;private transientValuesView values;private transientEntrySetView entrySet; // Unsafe mechanicsprivate static finalsun.misc.Unsafe U;private static final longSIZECTL;private static final longTRANSFERINDEX;private static final longBASECOUNT;private static final longCELLSBUSY;private static final longCELLVALUE;private static final longABASE;private static final intASHIFT; static{try{            U = sun.misc.Unsafe.getUnsafe();            Class k = ConcurrentHashMap.class;            SIZECTL = U.objectFieldOffset                (k.getDeclaredField("sizeCtl"));            TRANSFERINDEX = U.objectFieldOffset                (k.getDeclaredField("transferIndex"));            BASECOUNT = U.objectFieldOffset                (k.getDeclaredField("baseCount"));            CELLSBUSY = U.objectFieldOffset                (k.getDeclaredField("cellsBusy"));            Class ck = CounterCell.class;            CELLVALUE = U.objectFieldOffset                (ck.getDeclaredField("value"));            Class ak = Node[].class;            ABASE = U.arrayBaseOffset(ak);intscale = U.arrayIndexScale(ak);if((scale & (scale - 1)) != 0)throw newError("data type scale not a power of two");            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);        }catch(Exception e) {throw newError(e);        }    }}

```

上一篇下一篇

猜你喜欢

热点阅读