javaspring菜鸟教程,只需一篇文章吃透Java多线程技术
Spring框架自诞生以来一直备受开发者青睐,有人亲切的称之为:Spring 全家桶。Spring更是 避免了重复造轮子的工作并跟随着互联网行业的发展做出不断的更新 ,很多研发人员把spring看作心目中最好的Java项目,没有之一。
可以毫不夸张的说,Spring重新定义了Java,所以这是重点也是难点,工作中必须会,面试时肯定考,不过很多人在面试的时候在简历上写着熟悉Spring,但也不过只是会用SSM,在这个基础上做crud罢了,对于Spring全家桶里面其他技术点真的有所掌握吗?
Spring系列包含非常多的项目,可以满足Java开发中的方方面面,先来看下常用框架的知识点汇总:
接下来不多哔哔,直接上干货!
MySQL的复制默认是异步的,主从复制至少需要两个MYSQL服务,这些MySQL服务可以分布在不同的服务器上,也可以在同一台服务器上。
MySQL主从异步复制是最常见的复制场景。数据的完整性依赖于主库BINLOG的不丢失,只要主库的BINLOG不丢失,那么就算主库宕机了,我们还可以通过BINLOG把丢失的部分数据通过手工同步到从库上去。
注意:主库宕机的情况下,DBA可以通过mysqlbinlog工具手工访问主库binlog,抽取缺失的日志并同步到从库上去;也可以通过配置高可用MHA架构来自动抽取缺失的数据补全从库,或者启用Global Transaction Identifiers(GTID)来自动抽取缺失binlog到从库。
MySQL在BINLOG中记录事务(或SQL语句),也就是说对于支持事务的的引擎(例如InnoDB)来说,每个事务提交时都需要写BINLOG;对于不支持事务的引擎(例如MyISAM)来说,每个SQL语句执行完成时,都需要些BINLOG。为了保证Binlog的安全,MySQL引入sync_binlog参数来控制BINLOG刷新到磁盘的频率。
- 在默认情况下,sync_binlog=1,表示事务提交之前,MySQL都需要先把BINLOG刷新到磁盘,这样的话,即使出现数据库主机操作系统崩溃或者主机突然掉电的情况,系统最多损失prepared状态的事务;设置sync_binlog=1,尽可能保证数据安全。
- sync_binlog=0,表示MySQL不控制binlog的刷新,由文件系统自己控制文件缓存的刷新。
- sync_binlog=N,如果N不等于0或者1,刷新方式同sync_binlog=1类似,只不过此时会延长刷新频率至N次binlog提交组之后。
以上是传统的异步复制,在MySQL5.7的并行复制技术(也称多线程复制)到来之前,为人诟病最多的还是效率问题,slave延迟是一个顽疾,虽然之前已经出现了schema级别的并行复制,但实际效果并不好。
在MySQL5.7中,带来了全新的 多线程复制 技术,解决了当master同一个schema下的数据发生了变更,从库不能并发应用的问题,同时也真正将binlog组提交的优势充分发挥出来,保障了从库并发应用Relay Log的能力。
在MySQL8.0中,多线程复制又进行了技术更新,引入了writeset的概念,而在之前的版本中,如果主库的同一个会话顺序执行多个不同相关对象的事务,例如,先执行了Update A表的数据,又执行了Update B表的数据,那么BINLOG在复制到从库后,这两个事务是不能并行执行的,writeset的到来,突破了这个限制。
前面介绍的复制是异步操作,主库和从库的数据之间难免会存在一定的延迟,这样存在一个隐患:当在主库上写入一个事务并提交成功,而从库尚未得到主库的BINLOG日志时,主库由于磁盘损坏、内存故障、断电等原因意外宕机,导致主库上该事务BINLOG丢失,此时从库就会损失这个事务,从而造成主从不一致。
为了解决这个问题,从MySQL5.5开始,引入了半同步复制,此时的技术暂且称之为传统的半同步复制,因该技术发展到MySQL5.7后,已经演变为增强半同步复制(也成为无损复制)。在异步复制时,主库执行Commit提交操作并写入BINLOG日志后即可成功返回客户端,无需等待BINLOG日志传送给从库,如图所示。
而半同步复制时,为了保证主库上的每一个BINLOG事务都能够被可靠地复制到从库上,主库在每次事务成功提交时,并不及时反馈给前端应用用户,而是等待至少一个从库(详见参数rpl_semi_sync_master_wait_for_slave_count)也接收到BINLOG事务并成功写入中继日志后,主库才返回Commit操作成功给客户端(不管是传统的半同步复制,还是增强的半同步复制,目的都是一样的,只不过两种方式有一个席位地方不同,将在下面说明)
半同步复制保证了事务成功提交后,至少有两份日志记录,一份在主库的BINLOG日志上,另一份在至少一个从库的中继日志Relay Log上,从而更进一步保证了数据的完整性。
在传统的半同步复制中,主库写数据到BINLOG,且执行Commit操作后,会一直等待从库的ACK,即从库写入Relay Log后,并将数据落盘,返回给主库消息,通知主库可以返回前端应用操作成功,这样会出现一个问题,就是实际上主库已经将该事务Commit到了事务引擎层,应用已经可以可以看到数据发生了变化,只是在等待返回而已,如果此时主库宕机,有可能从库还没能写入Relay Log,就会发生主从库不一致。增强半同步复制就是为了解决这个问题,做了微调,即主库写数据到BINLOG后,就开始等待从库的应答ACK,直到至少一个从库写入Relay Log后,并将数据落盘,然后返回给主库消息,通知主库可以执行Commit操作,然后主库开始提交到事务引擎层,应用此时可以看到数据发生了变化。增强半同步复制的大致流程如下图所示。
半同步复制模式下,假如在传送BINLOG日志到从库时,从库宕机或者网络延迟,导致BINLOG并没有即使地传送到从库上,此时主库上的事务会等待一段时间(时间长短由参数rpl_semi_sync_master_timeout设置的毫秒数决定),如果BINLOG在这段时间内都无法成功发送到从库上,则MySQL自动调整复制为异步模式,事务正常返回提交结果给客户端。
半同步复制很大程度上取决于主从库之间的网络情况,往返时延RTT越小决定了从库的实时性越好。通俗地说,主从库之间的网络越快,从库越实时。
注意:往返时延RTT(Round-Trip Time)在计算机网络中是一个重要的性能指标,它表示从发送端发送数据开始到发送端接收到接收端的确认,总共经历的时长(这里可能有点拗口,我们可以理解为TCP三次握手的前两次握手)。
java集合详解
有时候需要存储一组数据,之前使用数组,但是数组具有固定的容量,但是在写程序时并不知道需要多少对象,在java.util包下提供了一套完整的集合类,包含List、Set、Queue、Map。java集合类都可以自动地调整自己的大小。
在创建集合时,经常使用泛型,可以在编译期防止将错误的类型放入到集合中。
集合概念
集合分为两个基本接口
- 集合(Collection):一个独立元素的序列,List必须已插入顺序保存元素,Set不能包含重复元素,Queue按照排队规则来确定对象产生的顺序(一般是插入顺序)
- 映射(Map):一组成对的\”键值对\”对象,允许使用键来查找值。map允许我们使用一个对象来查找另一个对象Arrays.asList()的输出是一个List,但是底层实现是数组,没法调整大小。
List
存储有序,可以重复的元素,相当于动态数组 集合中元素所在类要重写equals方法
- ArrayList
- LinkedList
- Vector
两种类型的list
- ArrayList:擅长随机访问元素,但在List中间插入和删除元素时速度较慢
- LinkedList:擅长在List中间进行插入和删除操作,提供了优化的顺序访问,对于随机访问相对较慢
List特性
- 允许插入重复元素
- 允许插入多个null元素
- List提供了ListIterator迭代器,可以提供双向访问
ArrayList和Vector的异同点
相同点
- 两者都是基于索引的,内部使用数组
- 两者维护插入顺序,可以根据插入顺序来获取元素
- ArrayList和Vector的迭代器实现都是fail-fast的
- ArrayList和Vector两者都允许null值,也可以使用索引值对元素进行随机访问
不同点
- Vector是同步的,ArrayList不是,但是已过时,使用CopyOnWriteArrayList
- ArrayList比Vector快
LinkedList链表
LinkedList添加了一些方法,使其可以被用作栈,队列和双向队列,方法差异
- getFirst()和element()是相同的,都是返回列表的头部,而并不删除它,如果list为空,则抛出NoSuchElementException异常。peek()方法在列表为空时返回null
- removeFirst()和remove()方法相同,删除并返回列表头部元素,在列表为空时返回NoSuchElementException异常,poll()在列表为空时返回null
- addFirst()在列表头部插入一个元素
- offer()和add()和addLast()相同,在列表尾部添加一个元素
- removeLast()删除并返回列表的最后一个元素
ArrayList和LinkedList的区别
- ArrayList是由数组支持的基于索引的数据结构,支持对元素的随机访问,复杂度为O(1),但是LinkedList是基于链表的,存储一系列的节点数据,每个节点都与前一个节点和下一个节点相连。虽然存在使用索引获取元素的方法,但是内部实现是从起始点开始遍历的,时间复杂度是O(n)
- 与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快
- LinkedList比ArrayList消耗更多内存,因为需要存储前后节点的引用
迭代器IteratorsIterator
Iterator接口提供了遍历任何Collection的接口,取代了java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除数据
iterator只能单向移动
- 使用iterator()方法使集合返回一个Iterator。Iterator将准备好返回序列中的第一个元素。
- 使用next()方法获得序列中的下一个元素。
- 使用hasNext()方法检查序列中是否含有元素。
- 使用remove()方法将迭代器最近返回的那个元素删除。
Enumeration和iterator的区别
- Enumeration的速度是Iterator的两倍,使用内存也少,但是iterator更加安全,使得一个集合在遍历时,会阻止其他线程去修改集合,Iterator允许移除元素
- Iterator支持fail-fast机制,而Enumeration不支持,Iterator遍历时,当其他线程修改集合内容时,迭代器会立马感知到,引起快速失败,抛出ConcurrentModificationException异常
- Enumeration本身不支持同步,只是在Vector和hashtable实现Enumeration时,添加了同步
ListIterator
- ListIterator是Iterator的子类型,只能由各种List类生成,
- Iterator只能向前移动,ListIterator可以双向移动,可以生成迭代器在列表中指向位置的后一个和前一个元素的索引。
堆栈stack
堆栈是后进先出(LIFO),最后压入(push)栈的元素,第一个被弹出(pop)栈。
java1.0中有一个stack类,但是设计的不好,Java6添加了ArrayDeque,其中包含了直接实现堆栈功能的方法
- push()添加元素到栈底
- peek()和pop()返回对象,peek()返回栈顶元素,但不从栈顶删除,而pop()删除并返回栈顶元素
Set
Set不保存重复的元素。查找是Set最重要的操作,选择HashSet实现,针对快速查找进行了优化。
存储无序,不可重复 添加Set集合中的元素所在类要重写equals和hashCode方法
无序性:指的是元素在底层存储的位置是无序的
- HashSet没有顺序,使用散列函数,HashSet维护顺序与TreeSet或LinkedHashSet不同,因为它们实现具有不同的元素存储方式
- LinkedHashSet 也使用了散列,使用了链表来维护元素的插入顺序,结果将按元素的插入顺序显示。元素必须定义hashCode()和equals()方法,遍历元素时,会按照添加的进去的顺序
- TreeSet将元素存储在红黑树数据结构,可以从Set中获取有序序列,其中元素必须实现Comparable接口要求添加进TreeSet的必须是同一个类的 两种排序方式 1)自然排序:添加的类要实现Comparable接口,重写compareTo方法 2)定制排序: 使用TreeSet(Comparator comparator) 构造器 重写compare(T o1, T o2);方法
Map
键值 key不可重复,一个key-value组成一个entry
map的分类
HashMap专为快速访问而设计,TreeMap保持键始终处于排序状态,没有HashMap快。LinkedHashMap按插入顺序保存其元素,但使用散列提供快速访问的能力。
- HashMap 基于哈希表的实现。为插入和定位键值对提供了常数时间性能。可以通过构造方法调整性能,这些构造方法允许设置哈希表的容量和装填因子。可以添加key为null,value为null
- LinkedHashMap 与HashMap类似,但是当遍历时,可以按照插入顺序或最近最少使用(LRU)顺序获取键值对。只比HashMap略慢,一个例外是在迭代时,由于其使用链表维护内部顺序,所以会更快些,按照添加进Map的顺序遍历
- TreeMap 基于红黑树实现,当查看键或键值对时,按排序顺序(由Comparable或Comparator确定)。TreeMap的侧重点在于按排序顺序获得结果。TreeMap是唯一使用subMap()方法的Map,返回红黑树的一部分,按照key所在类的指定属性进行排序,要求key是同一个类的对象(同TreeSet)
- WeakHashMap 一个具有弱键的Map,为了解决某些类型的问题,它允许释放Map所引用的对象。如果Map外没有对特定键的引用,则可以对该键进行垃圾回收
- ConcurrentHashMap 不使用同步锁定的线程安全Map
- IdentityHashMap 使用==来比较键,仅用于解决特殊问题
- HashTable 不可添加key为null,value为null的 子类Properties 处理属性文件
HashMap工作情况
HashMap在Map.Entry静态内部类实现存储键值对,HashMap使用哈希算法,在put和get方法中,使用hashCode和equals方法,使用put方法时,使用key的hashcode和哈希算法来找出存储键值对的索引,Entry存储在LinkedList中,如果存在entry,使用equals检查传递的key是否存在,如果存在,会覆盖掉value,如果不存在,会创建一个新的entry然后保存。get的时候也是先通过hashcode找到数组中的索引,然后使用equals找到正确的Entry,在进行取值
HashMap默认初始容量是32,负载因子是0.75,阈值是容量乘以负载因子,当map的大小比阈值大时,HashMap会对map的内容进行重新哈希。
HashMap和HashTable的区别
- HashMap允许key和value为null,HashTable不允许
- HashTable是同步的,HashMap不是
- HashMap可以转为LinkedHashMap,使得遍历有序,HashTable的顺序无法预知
- HashMap提供对key的set进行遍历,所以是fail-fast的,HashTable提供对key的Enumeration进行遍历,不支持fail-fast
- HashTable应该被CocurrentHashMap替代
队列队列操作
队列是一个先进先出(FIFO)集合,LinkedList实现了Queue接口,并且提供了一些方法支持队列行为
- offer()在队列尾部插入一个元素
- peek()和element()返回队列头而不删除它,如果队列为空,element()抛出NoSuchElementException,而peek()返回null
- poll()和remove()都删除并返回队头元素,如果队列为空,poll()返回null,remove()抛出NoSuchElementException
PriorityQueue优先级队列
优先级队列声明下一个弹出的元素是最需要的元素。
BlockingQueue队列
是concurrent包下的类,在进行检索或移除一个元素的时候,会等待队列变成非空;当添加一个元素的时候,会等待队列中的可用空间。主要用于实现生产者-消费者模式
Collections工具类unmodifiableCollection方法
Collections.unmodifiableCollection(list);Collections.unmodifiableList(list);使用该方法会创建一个只读集合,所有改变集合的操作都会抛出UnsupportedOperationException
synchronizedCollection方法
Collections.synchronizedCollection(list)方法可以创建一个线程安全的集合
问题1、遍历时移除List中的元素使用forEach和Iterator
在使用forEach遍历时,实际上是使用的Iterator,使用的核心方法是hasNext()和next(),但是使用的是list.remove,来看个例子
之前说过,Iterator在遍历时,不允许其他线程对该集合进行操作,看一下ArrayList的iterator是怎么实现的
在每次获取下一个元素时,都会比较modCount 和 expectedModCount
然后在调用的list的remove方法会导致modCount增加(modCount表示被修改次数)
此时iterator的next方法中两个变量就不一致了,就会抛出ConcurrentModificationException异常
再看一下如果使用iterator的remove方法
iterator在remove之后会将modCount的值赋给expectedModCount,就不会出现两个变量不等的情况了
不使用forEach遍历
使用普通for循环,有两种方式,第一种是使用正序遍历,但是进行remove操作之后要把遍历的索引进行修正减一,否则在移除下一个的时候就会出错,第二种就是使用倒序遍历
2、fail-fast和fail-safe
java.util包中集合类被设计为fail-fast的,而java.util.concurrent中集合为fail-safe的。fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException
3、Arrays.asList
这个方法返回的是一个ArrayList,不过这个ArrayList是Arrays类的内部类,在调用add方法的时候会直接报错
UnsupportedOperationException这是运行时异常
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。