一、string为何不可继承?html
答:由于string是用final修饰的不可变类,不能够被继承;java
为何要用final修饰?android
答:安全,设计者将string设为共享的,子类不能够修改,所以设置为final;web
效率,共享的效率更高;算法
String、StringBuffer和StringBuilder区别数据库
答:运行速度StringBuilder>StringBuffer>String,String为字符串常量,一旦建立对象后不可更改;然后者是可更改的;编程
线程安全上StringBuffer是线程安全的,StringBuilder是非线程安全的。跨域
二、怎样减小线程的上下文切换?数组
答:减小并发线程数,尽可能使用无锁编程,CAS算法浏览器
什么是CAS算法?
答:compare and swap意思是先比较旧的预期值和原来内存中存储的值相等,再更改成目标值,是一种乐观锁,适用于读多写少的状况,效率较高,只有极少的状况会发生冲突
CAS会有ABA问题应怎么解决?
答:ABA问题是指旧的预期值被更改成和原来内存中存储的值相等,已经发生过一次更改,这种状况能够经过版本号(AtomicStampedReference)、时间戳来解决,比较最初获取的版本号和更改前获取的版本号是否一致,一致则更改,更改后都将版本号+1
三、说下volatile关键字的做用?
答:可见性:当多个线程共同访问同一变量,当其中一个线程修改了该变量,其余线程都能马上看到修改的值
有序性:防止指令重排序,程序执行顺序按当前代码顺序执行
注:指令重排序不会影响单个线程的执行,但会影响多个线程并发执行的结果
https://www.cnblogs.com/dolphin0520/p/3920373.html
内存屏障会提供3个功能:
1)它确保指令重排序时不会把其后面的指令排到内存屏障以前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操做已经所有完成;
2)它会强制将对缓存的修改操做当即写入主存;
3)若是是写操做,它会致使其余CPU中对应的缓存行无效。
volatile能够保证原子性吗?
答:不是,两个线程同时从内存中读取变量的值i=10到本身的工做内存,线程a和线程b同时作自增操做+1,写入工做内存,再写入主内存,两个线程分别自增一次,但主内存中的结果只增长1,所以volatile不保证原子性
四、描述下java内存模型
答:Java内存模型规定全部的变量都是存在主存当中,每一个线程都有本身的工做内存。线程对变量的全部操做都必须在工做内存中进行,而不能直接对主存进行操做。而且每一个线程不能访问其余线程的工做内存。
五、描述下happens-before原则(先行发生原则)
答:Java内存模型具有一些先天的“有序性”
六、ThreadLocal 原理
定义:线程局部变量,为每个线程都提供了一份变量副本,每个线程均可以随意修改本身的变量副本,而不会对其余线程产生影响;
实现:在ThreadLocal类中有一个静态内部类ThreadLocalMap(其相似于Map),用键值对的形式存储每个线程的变量副本,ThreadLocalMap中元素的key为当前ThreadLocal对象,而value对应线程的变量副本,每一个线程可能存在多个ThreadLocal。
应用:采用了“以空间换时间”的方式,解决多线程数据共享问题,实际过程当中应用到了数据库链接。
问题:ThreadLocal有可能产生内存泄漏,由于map中的key弱引用指向ThreadLocal,当把threadlocal实例置为null之后,没有任何强引用指向threadlocal实例,因此threadlocal将会被gc回收. 可是,咱们的value却不能回收,由于存在一条从current thread链接过来的强引用。
七、synchronized锁怎么实现的?
答:synchronized锁存在Java对象头里,每个被锁住的对象都会和一个monitor对象关联;
执行monitorenter获取锁,读取monitor对象的count字段,count==0,则获取锁成功且count+1;
执行monitorexit指令时将count-1,若count==0,则释放锁;
synchronized为何是重量级锁?
答:重量级锁经过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操做系统的Mutex Lock实现,操做系统实现线程之间的切换须要从用户态到内核态的切换,切换成本很是高。
八、Lock锁怎么实现的?
ReentrantLock是基于AQS实现的,AQS的基础是CAS
AbstractQueuedSynchronizer,简称AQS,基于volatile int state +FIFO队列(双端队列)实现的,
ReentrantLock中有一个抽象类Sync,根据传入构造方法的布尔型参数实例化出Sync的实现类FairSync和NonfairSync,并重写tryAcquire(int arg)和tryRelease(int arg)两个方法。
lock()利用CAS去判断state是否是0,若是state=0,则获取锁,state设为1并将当前线程设为主;
若是state非0,执行acquire()->tryAcquire()->(非公平锁)若是state=0,CAS尝试获取锁,若加锁成功state设为1并将当前线程设为主,
state非0,判断当前owner是不是当前线程,若是是,state+1,返回true,若是不属于当前线程,返回false,则添加FIFO等待队列队尾。
(公平锁)若是state=0,hasQueuedPredecessors()判断当前是否有等待的线程, 若是没有使用CAS尝试获取锁,若加锁成功state设为1并将当前线程设为主,
state非0,判断当前owner是不是当前线程,若是是,state+1,返回true,若是不属于当前线程,返回false,则添加FIFO等待队列队尾。
ReentrantLock获取锁定与三种方式:
a) lock(), 若是获取了锁当即返回,若是别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁;
b) tryLock(), 若是获取了锁当即返回true,若是别的线程正持有锁,当即返回false;
c)tryLock(long timeout,TimeUnit unit),若是获取了锁定当即返回true,若是别的线程正持有锁,会等待参数给定的时间,在等待的过程当中,若是获取了锁定,就返回true,若是等待超时,返回false;
d)lockInterruptibly():若是获取了锁定当即返回,若是没有获取锁,当前线程处于休眠状态,直到获取锁,或者当前线程被别的线程中断;
九、synchronized和Lock什么区别?
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 经过代码实现,是一个类 |
锁的释放 | 会自动释放锁定(代码执行完成或者出现异常) | 不会主动释放,必须将unLock()放到finally{}中 |
锁的获取 | 假设A线程得到锁,B线程等待。若是A线程阻塞,B线程会一直等待 | 分状况而定,Lock有多个锁获取的方式,具体下面会说道,大体就是能够尝试得到锁,线程能够不用一直等待 |
锁状态 | 没法判断 | 能够判断 |
锁类型 | 可重入,不可中断,非公平 | 可重入,可中断,可公平(二者皆可) |
性能 | 少许同步 | 大量同步 |
十、线程池有哪几种?
答:Java经过Executors提供四种线程池,分别为:
一、newCachedThreadPool:建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。(线程最大并发数不可控制)SynchronousQueue
二、newFixedThreadPool:建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。LinkedBlockingQueue
三、newScheduledThreadPool:建立一个定长线程池,支持定时及周期性任务执行。
四、newSingleThreadExecutor:建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。LinkedBlockingQueue
线程池参数有哪些?
答:ThreadPoolExecutor构造方法
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//线程池中超过corePoolSize数目的空闲线程最大存活时间;能够allowCoreThreadTimeOut(true)成为核心线程的有效时间
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue,//阻塞任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler) {//当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
线程池的工做过程?
答:1.当线程池小于corePoolSize时,新提交任务将建立一个新线程执行任务,即便此时线程池中存在空闲线程;
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行;
3.当workQueue已满,且当前线程数大于corePoolSize且小于maximumPoolSize时,新提交任务会建立新线程执行任务;
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理;
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程;
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭;
线程池拒绝策略有几种?
答:ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,可是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,而后从新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池相关的几个队列:
答:ArrayBlockingQueue, 一个由数组结构组成的有界阻塞队列fifo;默认状况下不保证公平阻塞,能够经过配置建立公平阻塞的队列,实现原理是可重入锁。
LinkedBlockingQueue,一个由链表结构组成的有界阻塞队列 fifo,LinkedBlockingQueue 可无界,也能够有界,初始化指定长度则为有界,不指定则为无界
PriorityBlockingQueue,一个支持优先级排序的无界阻塞队列
synchronousQueue,一个不存储元素的阻塞队列
十一、线程状态有几种?
答:线程的生命周期为5个状态,新建状态、就绪状态(start)、运行状态(run)、阻塞状态和死亡状态。
运行的线程执行sleep线程进入阻塞状态,sleep时间到了之后进入就绪状态。
线程的实现由3种方法:继承Tread类;实现Runnable接口;实现Callable接口
Sleep和wait的区别:sleep属于Thread类,wait属于Object类,
sleep线程不会释放对象锁,wait会释放对象锁;
wait须要调用notify()方法后本线程才进入对象索定池(只能同步方法或同步代码块中使用),sleep结束后进入就绪状态。
notify()与notifyAll()的区别:notify()随机通知一个线程;
notifyAll()通知全部线程;
十一、Java8新特性
答:(1) lambda表达式 (2) Stream,处理集合数据(3) 接口default方法,有利于数据移植
十二、抽象类和接口
答:抽象类和抽象方法都须要被abstract修饰,抽象方法必定要定义在抽象类中。抽象类全部方法都是抽象方法的时候,彻底能够用接口实现。抽象类为部分方法提供实现,避免了子类重复实现这些方法,提供了代码重用性。单继承,多实现。既要定义子类的行为,又要为子类提供共性功能时才选用抽象类。
1三、内部类
静态内部类:static修饰,能够访问外部类全部的静态变量和方法。应用场景如hashmap的静态内部类entry
成员内部类:能够访问外部类全部的变量和方法。和静态内部类不一样,成员内部类的实例都依赖外部类的实例。
局部内部类:即定义在方法中的类,能够访问外部类的全部变量和方法,若是局部内部类用static修饰方法内,则只能访问外部类的static变量
匿名内部类:匿名内部类能够出如今任何容许表达式出现的地方,定义格式为new 类/接口。应用场景,分布式工具类,多线程,android中的绑定监听事件。
1三、反射
反射是java在运行状态下,对于任意一个类,都可以知道这个类的全部属性和方法;
优势是能够动态建立对象和编译;缺点是对性能有影响。
获取方式有3种:类名.class,Class.forName(xxx); new 类名.getclass()。
反射的应用场景有:反编译、mybatis底层实现、工具类,bean转map,经过反射获取注解,能够作一些校验
1四、java面向对象的三大特性
继承:是对有共同特性的多类事物,进行再抽象成一个类。java继承是extends关键字,子类中有和父类中可访问的同名同返回同参数列表的方法时,就会覆盖从父类继承来的方法
封装:隐藏对象的属性和实现细节,仅对外提供接口。java中的类属性访问权限不是private,要想隐藏该类方法须要用private修饰符
多态:两种机制:编译时多态和运行时多态.
方法重载,方法名相同,其余不一样,编译时重载。方法重写(覆盖),子类覆盖父类的方法。
1五、泛型
泛型只在编译阶段有效,在编译过程当中,正确检测泛型结果后,会将泛型的相关信息擦除,而且添加类型检测和类型转换。
使用泛型的好处是:类型安全;消除强制类型转换,提升性能。
泛型的具体使用有3种方式:
(1) 泛型类,例如hashmap<K,V>,其中key和value只在运行时才会真正根据类型来构造和分配内存
(2) 泛型接口,当2个接口的处理方式一致,只是对象不一样的时候,能够用泛型接口
(3) 泛型方法,把方法参数泛型话
1六、join是怎么实现的?
join是thread类synchronized修饰的同步方法,其使用wait()方法实现等待;
1七、异常
1、error
程序没法处理的错误,比较严重的问题,虚拟机错误,如OutOfMemoryError、NoClassDefFoundError
2、exception
一、已检查异常
编译器要求必须对代码try catch或者throw expetion,如IOException、SQLException、FileNotFoundException、ParseException
二、未检查异常(RuntimeException运行时异常)
编译时不检查,运行时可能发生的异常,如NullPointerException,IndexOutOfBoundsException
1八、有return的状况下try catch finally的执行顺序:
(1)无论有没有出现异常,当执行到try内时,finally块中代码都会执行;
(2)当try和catch中有return时,finally仍然会执行;
(3)finally是在return后面的表达式运算后执行的(此时并无返回运算后的值,而是先把要返回的值保存起来,无论finally中的代码怎么样,返回的值都不会改变(对象类型须要另外分析),仍然是以前保存的值),因此函数返回值是在finally执行前肯定的;
(4)finally中最好不要包含return,不然程序会提早退出,返回值不是try或catch中保存的返回值。
1九、对象的初始化顺序:
(1)类加载以后,按从上到下(从父类到子类)执行被static修饰的语句;
(2)当static语句执行完以后,再执行main方法;
(3)若是有语句new了自身的对象,将从上到下执行构造代码块、构造器(二者能够说绑定在一块儿)。
20、cookie与session
Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的链接就会关闭,再次交换数据须要创建新的链接。这就意味着服务器没法从链接上跟踪会话。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它能够弥补HTTP协议无状态的不足。在Session出现以前,基本上全部的网站都采用Cookie来跟踪会话。
Cookie其实是一小段的文本信息。客户端请求服务器,若是服务器须要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。
Cookie具备不可跨域名性。
ookie生命周期默认为浏览器会话期间,驻留内存,关闭浏览器cookie就没了
设置cookie的过时时间为永不过时,将cookie保存在硬盘上
首先浏览器请求服务器访问web站点时,程序须要为客户端的请求建立一个session的时候,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,若是已经包含了一个sessionid则说明之前已经为此客户端建立过session,服务器就按照sessionid把这个session检索出来使用,若是客户端请求不包含session id,则服务器为此客户端建立一个session而且生成一个与此session相关联的session id,sessionid 的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionid将在本次响应中返回到客户端保存,保存这个sessionid的方式就能够是cookie,这样在交互的过程当中,浏览器能够自动的按照规则把这个标识发回给服务器,服务器根据这个sessionid就能够找获得对应的session,又回到了这段文字的开始
关闭浏览器不会致使session被删除,迫使服务器为seesion设置了一个失效时间,通常是30分钟
当浏览器将cookie禁用,基于cookie的session将不能正常工做,每次使用request.getSession() 都将建立一个新的session。达不到session共享数据的目的,可是咱们知道原理,只须要将session id 传递给服务器session就能够正常工做的。
解决:经过URL将session id 传递给服务器:URL重写
2一、通讯方式
1、进程间的通讯方式
# 管道( pipe ):管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程关系。
# 有名管道 (namedpipe) : 有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。
# 信号量(semophore ) : 信号量是一个计数器,能够用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。
# 消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 信号 (sinal ) : 信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。
# 共享内存(shared memory ) :共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的 IPC 方式,它是针对其余进程间通讯方式运行效率低而专门设计的。它每每与其余通讯机制,如信号量,配合使用,来实现进程间的同步和通讯。
# 套接字(socket ) : 套解口也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样及其间的进程通讯。
2、线程间的通讯方式
# 锁机制:包括互斥锁、条件变量、读写锁
*互斥锁提供了以排他方式防止数据结构被并发修改的方法。
*读写锁容许多个线程同时读共享数据,而对写操做是互斥的。
*条件变量能够以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一块儿使用。
# 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
# 信号机制(Signal):相似进程间的信号处理
线程间的通讯目的主要是用于线程同步,因此线程没有像进程通讯中的用于数据交换的通讯机制。
2二、使用DateTimeFormatter
若是是Java8应用,可使用DateTimeFormatter代替SimpleDateFormat,这是一个线程安全的格式化工具类。