Java整理

 

1.JVM调优:堆大小设置,回收器选择,辅助信息html

2.线程池的实现:java

由核心池和任务队列组成,先向核心池中提交任务,而后向任务队列中提交。mysql

当线程池中的核心线程数已满时,任务就要保存到队列中了。web

线程池中使用的队列是 BlockingQueue 接口,经常使用的实现有以下几种:算法

  • ArrayBlockingQueue:基于数组、有界,按 FIFO(先进先出)原则对元素进行排序
  • LinkedBlockingQueue:基于链表,按FIFO (先进先出) 排序元素 
    • 吞吐量一般要高于 ArrayBlockingQueue
    • Executors.newFixedThreadPool() 使用了这个队列
  • SynchronousQueue:不存储元素的阻塞队列 
    • 每一个插入操做必须等到另外一个线程调用移除操做,不然插入操做一直处于阻塞状态
    • 吞吐量一般要高于 LinkedBlockingQueue
    • Executors.newCachedThreadPool使用了这个队列
  • PriorityBlockingQueue:具备优先级的、无限阻塞队列

饱和策略分为:spring

  • Abort 策略,:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
  • CallerRuns 策略:为调节机制,既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。
  • Discard策略:新提交的任务被抛弃。
  • DiscardOldest策略:丢弃的是“队头”的任务,而后尝试提交新的任务。(不适合工做队列为优先队列场景)

三、spring bean的六个做用域:singleton、prototype、request、session、application、websocketsql

4.Java垃圾回收机制:数据库

经过引用计数来判断一个对象是否能够被回收。这种方式的特色是实现简单,并且效率较高,可是它没法解决循环引用的问题,所以在Java中并无采用这种方式(Python采用的是引用计数法)。编程

在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是经过一系列名为GC Roots的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,因此它们将会断定为是可回收对象。设计模式

JVM内存模型:

名称 特征 做用 配置 异常
栈区 线程私有,使用一段连续的内存空间 存放局部变量表、操做栈、动态连接、方法出口 -XSs StackOverflowError OutOfMemoryError
线程共享,生命周期与虚拟机相同 保存对象实例 -Xms -Xmx -Xmn OutOfMemoryError
程序计数器 线程私有、占用内存小 字节码行号
方法区 线程共享 存储类加载信息、常量、静态变量等 -XX:PermSize -XX:MaxPermSize OutOfMemoryError

典型的垃圾回收算法:

新生代对象】:存放年轻对象的堆空间,年轻对象指刚刚建立,或者经历垃圾回收次数很少的对象。新生代使用复制和标记-清除垃圾收集算法

老年代对象】:存放老年对象的堆空间。即为经历屡次垃圾回收依然存活的对象。所以在年老代中使用标记-整理垃圾回收算法。

【永久代】:永久代也使用标记-整理算法进行垃圾回收

1.Mark-Sweep(标记-清除)算法

标记清除算法可能产生的最大的问题就是空间碎片

2.Copying(复制)算法

算法思想:将原有的内存空间分为两块相同的存储空间,每次只使用一块,在垃圾回收时,将正在使用的内存块中存活对象复制到未使用的那一块内存空间中,以后清除正在使用的内存块中的全部对象,完成垃圾回收。

3.Mark-Compact(标记-压缩)算法

复制算法的高效性是创建在存活对象少、垃圾对象多的状况下,这种状况在新生代比较常见,

可是在老年代中,大部分对象都是存活的对象,若是仍是有复制算法的话,成本会比较高。所以,基于老年代这种特性,应该使用其余的回收算法。

4.Generational Collection(分代收集)算法

分代算法思想:将内存空间根据对象的特色不一样进行划分,选择合适的垃圾回收算法,以提升垃圾回收的效率。

5.分区算法

算法思想:分区算法将整个堆空间划分为连续的不一样小区间,

每个小区间都独立使用,独立回收。

 

一、强引用
    若是一个对象具备强引用,GC毫不会回收它;当内存空间不足,JVM宁愿抛出OutOfMemoryError错误。通常new出来的对象都是强引用

二、软引用
     若是一个对象具备软引用,当内存空间不足,GC会回收这些对象的内存,使用软引用构建敏感数据的缓存。

三、弱引用  
     若是一个对象具备弱引用,在GC线程扫描内存区域的过程当中,无论当前内存空间足够与否,都会回收内存,利用jdk中的ThreadLocal就是弱引用的,具体间下面的详细说明。

四、虚引用
     若是一个对象仅持有虚引用,在任什么时候候均可能被垃圾回收,虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列联合使用,虚引用主要用来跟踪对象 被垃圾回收的活动。

 

Java类的加载过程:

加载->连接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载 

类的初始化顺序 :(静态变量、静态初始化块)–>(变量、初始化块)–> 构造器;若是有父类,则顺序是:父类static方法 –> 子类static方法 –> 父类构造方法- -> 子类构造方法 

 

什么是事务(Transaction)?

是指做为单个逻辑工做单元执行的一系列操做,要么彻底地执行,要么彻底地不执行。 事务处理能够确保除非事务性单元内的全部操做都成功完成,不然不会永久更新面向数据的资源。经过将一组相关操做组合为一个要么所有成功要么所有失败的单元,能够简化错误恢复并使应用程序更加可靠。一个逻辑工做单元要成为事务,必须知足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的一个逻辑工做单位,由DBMS中的事务管理子系统负责事务的处理。

举个例子加深一下理解:同一个银行转帐,A转1000块钱给B,这里存在两个操做,一个是A帐户扣款1000元,两一个操做是B帐户增长1000元,二者就构成了转帐这个事务。

  • 两个操做都成功,A帐户扣款1000元,B帐户增长1000元,事务成功
  • 两个操做都失败,A帐户和B帐户金额都没变,事务失败

最后思考一下,怎么样会出现A帐户扣款1000元,B帐户金额不变?若是你是把两个操做放在一个事务里面,而且是数据库提供的内在事务支持,那就不会有问题,可是开发人员把两个操做放在两个事务里面,而第二个事务失败就会出现中间状态。现实中本身实现的分布式事务处理不当也会出现中间状态,这并非事务的错,事务自己就是规定不会出现中间状态,是事务实现者作出来的方案有问题。

事务的4个特性

  • 原子性(Atomic):事务必须是原子工做单元;对于其数据修改,要么全都执行,要么全都不执行。一般,与某个事务关联的操做具备共同的目标,而且是相互依赖的。若是系统只执行这些操做的一个子集,则可能会破坏事务的整体目标。原子性消除了系统处理操做子集的可能性。

  • 一致性(Consistency):事务的一致性指的是在一个事务执行以前和执行以后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态知足全部的完整性约束,就说该数据库是一致的。

  • 隔离性(Isolation):由并发事务所做的修改必须与任何其它并发事务所做的修改隔离。事务查看数据时数据所处的状态,究竟是另外一个事务执行以前的状态仍是中间某个状态,相互之间存在什么影响,是能够经过隔离级别的设置来控制的。

  • 持久性(Durability):事务结束后,事务处理的结果必须可以获得固化,即写入数据库文件中即便机器宕机数据也不会丢失,它对于系统的影响是永久性的。

事务并发控制

咱们从另一个方向来讲说,若是不对事务进行并发控制,咱们看看数据库并发操做是会有那些异常情形,有些使咱们能够接受的,有些是不能接受的,注意这里的异常就是特定语境下的,并不必定就是错误什么的。假设有一个order表,有个字段叫count,做为计数用,当前值为100

  • 第一类丢失更新(Update Lost):此种更新丢失是由于回滚的缘由,因此也叫回滚丢失。此时两个事务同时更新count,两个事务都读取到100,事务一更新成功并提交,count=100+1=101,事务二出于某种缘由更新失败了,而后回滚,事务二就把count还原为它一开始读到的100,此时事务一的更新就这样丢失了。

  • 脏读(Dirty Read):此种异常时由于一个事务读取了另外一个事务修改了可是未提交的数据。举个例子,事务一更新了count=101,可是没有提交,事务二此时读取count,值为101而不是100,而后事务一出于某种缘由回滚了,而后第二个事务读取的这个值就是噩梦的开始。

  • 不可重复读(Not Repeatable Read):此种异常是一个事务对同一行数据执行了两次或更屡次查询,可是却获得了不一样的结果,也就是在一个事务里面你不能重复(即屡次)读取一行数据,若是你这么作了,不能保证每次读取的结果是同样的,有可能同样有可能不同。形成这个结果是在两次查询之间有别的事务对该行数据作了更新操做。举个例子,事务一先查询了count,值为100,此时事务二更新了count=101,事务一再次读取count,值就会变成101,两次读取结果不同。

  • 第二类丢失更新(Second Update Lost):此种更新丢失是由于更新被其余事务给覆盖了,也能够叫覆盖丢失。举个例子,两个事务同时更新count,都读取100这个初始值,事务一先更新成功并提交,count=100+1=101,事务二后更新成功并提交,count=100+1=101,因为事务二count仍是从100开始增长,事务一的更新就这样丢失了。

  • 幻读(Phantom Read):幻读和不可重复读有点像,只是针对的不是数据的值而是数据的数量。此种异常是一个事务在两次查询的过程当中数据的数量不一样,让人觉得发生幻觉,幻读大概就是这么得来的吧。举个例子,事务一查询order表有多少条记录,事务二新增了一条记录,而后事务一查了一下order表有多少记录,发现和第一次不同,这就是幻读。

数据库事务隔离级别

看到上面提到的几种问题,你可能会想,我擦,这么多坑怎么办啊。其实上面几种状况并非必定都要避免的,具体看你的业务要求,包括你数据库的负载都会影响你的决定。不知道你们发现没有,上面各类异常状况都是多个事务之间相互影响形成的,这说明两个事务之间须要某种方式将他们从某种程度上分开,下降直至避免相互影响。这时候数据库事务隔离级别就粉墨登场了,而数据库的隔离级别实现通常是经过数据库锁实现的。

  • 读未提交(Read Uncommitted):该隔离级别指即便一个事务的更新语句没有提交,可是别的事务能够读到这个改变,几种异常状况均可能出现。极易出错,没有安全性可言,基本不会使用。

  • 读已提交(Read Committed):该隔离级别指一个事务只能看到其余事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,如Oracle,Sqlserver。

  • 可重复读(Repeatable Read):该隔离级别指一个事务中进行两次或屡次一样的对于数据内容的查询,获得的结果是同样的,但不保证对于数据条数的查询是同样的,只要存在读改行数据就禁止写,消除了不可重复读和第二类更新丢失,这是Mysql数据库的默认隔离级别。

  • 串行化(Serializable):意思是说这个事务执行的时候不容许别的事务并发执行.彻底串行化的读,只要存在读就禁止写,但能够同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,可是效率过低,通常不会用。

下面是各类隔离级别对各异常的控制能力:

级别\异常 第一类更新丢失 脏读 不可重复读 第二类丢失更新 幻读
读未提交 Y Y Y Y Y
读已提交 N N Y Y Y
可重复读 N N N N Y
串行化 N N N N N

数据库锁分类

通常能够分为两类,一个是悲观锁,一个是乐观锁,悲观锁通常就是咱们一般说的数据库锁机制,乐观锁通常是指用户本身实现的一种锁机制,好比hibernate实现的乐观锁甚至编程语言也有乐观锁的思想的应用。

悲观锁:顾名思义,就是很悲观,它对于数据被外界修改持保守态度,认为数据随时会修改,因此整个数据处理中须要将数据加锁。悲观锁通常都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁,表锁不管是读写锁都是悲观锁。

悲观锁按照使用性质划分:

  • 共享锁(Share locks简记为S锁):也称读锁,事务A对对象T加s锁,其余事务也只能对T加S,多个事务能够同时读,但不能有写操做,直到A释放S锁。

  • 排它锁(Exclusivelocks简记为X锁):也称写锁,事务A对对象T加X锁之后,其余事务不能对T加任何锁,只有事务A能够读写对象T直到A释放X锁。

  • 更新锁(简记为U锁):用来预约要对此对象施加X锁,它容许其余事务读,但不容许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。由于使用共享锁时,修改数据的操做分为两步,首先得到一个共享锁,读取数据,而后将共享锁升级为排它锁,而后再执行修改操做。这样若是同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就形成了死锁。若是一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就能够避免死锁。

悲观锁按照做用范围划分:

  • 行锁:锁的做用范围是行级别,数据库可以肯定那些行须要锁的状况下使用行锁,若是不知道会影响哪些行的时候就会使用表锁。举个例子,一个用户表user,有主键id和用户生日birthday当你使用update … where id=?这样的语句数据库明确知道会影响哪一行,它就会使用行锁,当你使用update … where birthday=?这样的的语句的时候由于事先不知道会影响哪些行就可能会使用表锁。
  • 表锁:锁的做用范围是整张表。

乐观锁:顾名思义,就是很乐观,每次本身操做数据的时候认为没有人回来修改它,因此不去加锁,可是在更新的时候会去判断在此期间数据有没有被修改,须要用户本身去实现。既然都有数据库提供的悲观锁能够方便使用为何要使用乐观锁呢?对于读操做远多于写操做的时候,大多数都是读取,这时候一个更新操做加锁会阻塞全部读取,下降了吞吐量。最后还要释放锁,锁是须要一些开销的,咱们只要想办法解决极少许的更新操做的同步问题。换句话说,若是是读写比例差距不是很是大或者你的系统没有响应不及时,吞吐量瓶颈问题,那就不要去使用乐观锁,它增长了复杂度,也带来了额外的风险。

乐观锁实现方式:

  • 版本号(记为version):就是给数据增长一个版本标识,在数据库上就是表中增长一个version字段,每次更新把这个字段加1,读取数据的时候把version读出来,更新的时候比较version,若是仍是开始读取的version就能够更新了,若是如今的version比老的version大,说明有其余事务更新了该数据,并增长了版本号,这时候获得一个没法更新的通知,用户自行根据这个通知来决定怎么处理,好比从新开始一遍。这里的关键是判断version和更新两个动做须要做为一个原子单元执行,不然在你判断能够更新之后正式更新以前有别的事务修改了version,这个时候你再去更新就可能会覆盖前一个事务作的更新,形成第二类丢失更新,因此你可使用update … where … and version=”old version”这样的语句,根据返回结果是0仍是非0来获得通知,若是是0说明更新没有成功,由于version被改了,若是返回非0说明更新成功。
  • 时间戳(timestamp):和版本号基本同样,只是经过时间戳来判断而已,注意时间戳要使用数据库服务器的时间戳不能是业务系统的时间。
  • 待更新字段:和版本号方式类似,只是不增长额外字段,直接使用有效数据字段作版本控制信息,由于有时候咱们可能没法改变旧系统的数据库表结构。假设有个待更新字段叫count,先去读取这个count,更新的时候去比较数据库中count的值是否是我指望的值(即开始读的值),若是是就把我修改的count的值更新到该字段,不然更新失败。java的基本类型的原子类型对象如AtomicInteger就是这种思想。
  • 全部字段:和待更新字段相似,只是使用全部字段作版本控制信息,只有全部字段都没变化才会执行更新。

    乐观锁几种方式的区别:

    新系统设计可使用version方式和timestamp方式,须要增长字段,应用范围是整条数据,不论那个字段修改都会更新version,也就是说两个事务更新同一条记录的两个不相关字段也是互斥的,不能同步进行。旧系统不能修改数据库表结构的时候使用数据字段做为版本控制信息,不须要新增字段,待更新字段方式只要其余事务修改的字段和当前事务修改的字段没有重叠就能够同步进行,并发性更高。

mysql事务隔离级别实战

实践是检验真理的惟一标准,掌握上面的理论以后,咱们在数据库上实战一番家里更好地掌握也加深理解,同时有助于解决实际问题。不一样数据库不少实现可能不一样,这里以mysql为例讲解各类隔离级别下的状况,测试表为user(id,name,gender,passwd,email)。

隔离级别:read-uncommitted

脏读测试流程: 
1. A设置隔离级别为read-uncommitted(注意这里未声明都是session级别,而非全局的),开启事务,查询id=1的记录 
2. B设置隔离级别为read-uncommitted,开启事务,修改id=1的记录,但不提交 
3. A再次查询id=1的记录,和第一次查询的比较一下 
4. B事务回滚,A事务回滚。

A:

这里写图片描述

B:

这里写图片描述

结论:A读到了B没有提交的内容,隔离级别为read-uncommitted的时候出现脏读。

第一类更新丢失测试流程: 
1. A设置隔离级别为read-uncommitted,开启事务,查询id=1的记录 
2. B设置隔离级别为read-uncommitted,开启事务,查询id=1的记录 
3. A修改id=1的记录 
4. B修改id=1的记录 
5. A提交 
6. B回滚 
7. A在查询一次id=1的记录,看看本身的修改是否成功

结论:结果不如我所想的,A的更新成功了,为何呢?A执行update语句的时候对该条记录加锁了,B这时候根本没法修改直至超时,也就是至少在mysql中在read-uncommitted隔离级别下验证第一类丢失更新,据了解有的数据库好像能够设置不加锁,若是可以不加锁的话则能够实现,也贴一下图吧。

A:

这里写图片描述

B:

这里写图片描述

不可重复读测试流程(省略):

结论:流程和测试脏读同样,其实在第一次测试脏读的时候就能够发现会出现不可重复读,A两次读取id=1的数据内容不一样。

第二类丢失更新流程: 
1. A开启事务,查询order_id=1的记录 
2. B开启事务,查询order_id=1的记录 
3. A把查出来的count加1后更新 
4. B把查出来的count加1更新 
5. A提交,B也提交

A:

这里写图片描述

B:

这里写图片描述

结论:A的更新丢失,咱们但愿的结果是3,而实际结果是2,跟java的多线程很像对不对,read-uncommitted隔离模式下会出现第二类丢失更新。

幻读测试流程: 
1. A开启事务,查询user表全部数据 
2. B开启事务,新增一条记录 
3. A再次查询user表全部记录,和第一次做比对 
4. A回滚,B回滚

A:

这里写图片描述

B:

这里写图片描述

结论:A两次查询全表数据结果不一样,read-uncommitted隔离模式下会出现幻读。

注:由于后面对这几种异常状况的测试流程基本和上面同样,个别有些差异读者本身注意,另外注意更改隔离级别便可,就能看到对应结果,后面的我只给出进一步能解决的异常测试截图,结论能够参照前面的对照表。

隔离级别:read-committed

脏读测试截图

A:

这里写图片描述 
B:

这里写图片描述

结论:A没有读到B没有提交的内容,隔离级别为read-committed的时候不会出现脏读。

隔离级别:repeatable-read

不可重复读测试截图

A:

这里写图片描述

B:

这里写图片描述

结论:A两次读取id=1的数据内容相同,repeatable-read隔离模式下不会出现不可重复读。

隔离级别:Serializable

幻读测试截图

A:

这里写图片描述

B:

这里写图片描述.

结论:由于A事务未提交以前,B事务插入操做没法得到锁而超时,Serializable隔离模式下不会出现幻读

spring AOP

一 AOP的基本概念

(1)Aspect(切面):一般是一个类,里面能够定义切入点和通知

(2)JointPoint(链接点):程序执行过程当中明确的点,通常是方法的调用

(3)Advice(通知):AOP在特定的切入点上执行的加强处理,有before,after,afterReturning,afterThrowing,around

(4)Pointcut(切入点):就是带有通知的链接点,在程序中主要体现为书写切入点表达式

(5)AOP代理:AOP框架建立的对象,代理就是目标对象的增强。Spring中的AOP代理可使JDK动态代理,也能够是CGLIB代理,前者基于接口,后者基于子类

 

动态代理

spring事务传播特性。

传播行为

含义

PROPAGATION_REQUIRED(XML文件中为REQUIRED)

表示当前方法必须在一个具备事务的上下文中运行,若有客户端有事务在进行,那么被调用端将在该事务中运行,不然的话从新开启一个事务。(若是被调用端发生异常,那么调用端和被调用端事务都将回滚)

PROPAGATION_SUPPORTS(XML文件中为SUPPORTS)

表示当前方法没必要须要具备一个事务上下文,可是若是有一个事务的话,它也能够在这个事务中运行

PROPAGATION_MANDATORY(XML文件中为MANDATORY)

表示当前方法必须在一个事务中运行,若是没有事务,将抛出异常

PROPAGATION_NESTED(XML文件中为NESTED)

表示若是当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务能够独立于被封装的事务中进行提交或者回滚。若是封装事务存在,而且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。若是封装事务不存在,则同PROPAGATION_REQUIRED的同样

PROPAGATION_NEVER(XML文件中为NEVER)

表示当方法务不该该在一个事务中运行,若是存在一个事务,则抛出异常

PROPAGATION_REQUIRES_NEW(XML文件中为REQUIRES_NEW)

表示当前方法必须运行在它本身的事务中。一个新的事务将启动,并且若是有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

PROPAGATION_NOT_SUPPORTED(XML文件中为NOT_SUPPORTED)

表示该方法不该该在一个事务中运行。若是有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行

 

强一致性、顺序一致性、弱一致性和共识http://www.javashuo.com/article/p-reapojwo-ec.html

 

Java并发编程:volatile关键字解析 http://www.javashuo.com/article/p-hsugcpqq-hw.html

相关文章
相关标签/搜索