java知识体系⭐
2019-09-03 10:57:16 7 举报
AI智能生成
Java知识体系
作者其他创作
大纲/内容
javaSE
集合框架
Collection接口
List接口
ArrayList
底层由数组实现,非线程安全,能实现动态扩容(扩容到原来的两倍)
LinkedList
底层是双向链表
Vector
Stack
在Vector基础上,增加了push、pop等方法实现LIFO(Last In First Out)后进先出的访问方式
底层是数组,且线程安全(synchronized关键字修饰方法);支持动态扩容(扩容为原来的1.5倍)
Set接口
HashSet
LinkedHashSet
SortedSet接口
TreeSet
Map接口
HashMap
LinkedHashMap
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。
扩展机制,大于负载因子(默认0.75),一倍扩容,重新计算hash位置
HashTable
与HashMap的主要区别:
* HashMap中键(key)和值(value)均可以为null,而Hashtable则不允许。
原因:由JDK源码可知,HashMap中key为null时hashCode取0,value没做限制;
而Hashtable中key和value为null时均会出现NullPointerException。
*方法加了synchronized关键字,线程安全
其中的对象必须实现equals和hashCode方法 java.util.Properties是其子类
* HashMap中键(key)和值(value)均可以为null,而Hashtable则不允许。
原因:由JDK源码可知,HashMap中key为null时hashCode取0,value没做限制;
而Hashtable中key和value为null时均会出现NullPointerException。
*方法加了synchronized关键字,线程安全
其中的对象必须实现equals和hashCode方法 java.util.Properties是其子类
SortedMap接口
Map接口的有序子接口,按照键的自然顺序或指定排序规则Comparator进行排序
TreeMap
实现了SortedMap接口
* 底层就是红黑树算法的实现
* 线程不安全
* 底层就是红黑树算法的实现
* 线程不安全
WeakHashMap
其中的键是弱键,当它不被使用时会自动被GC给回收,同时其所在的entry节点会被删除
Collections工具类
提供了常用的集合操作方法,如排序sort等方法;同时还提供常用集合的同步实现,以适应多线程环境
数据结构
链表
单向链表
链表中的每个结点只包含一个指针,指向其后继结点。最后一个结点的指针为空。
循环链表
链表中的最后一个结点的指针指向了头结点,因此整个链表形成一个环。解决了单链表的单向性问题。
双向链表
在双向链表中,每个结点都有两个指针,分别指向其前驱结点和后继结点。同样解决了单链表的单向性问题。
红黑树
跳跃表
多线程
线程和进程
线程是程序的最小执行单元
进程是操作系统进行资源分配和调度的一个基本单位
关联:一个程序至少有一个进程,一个进程又至少包含一个线程
引入线程的目的是充分利用CPU资源,使其可以并行处理多个任务,减少时间消耗
线程分类
用户线程User Thread
一般是程序中创建的线程
守护线程Daemon Thread
为用户线程服务的线程当所有非守护线程结束时才会被终止。如JVM的垃圾回收、内存管理等。
通过thread.setDaemon(true)来将一个线程变成守护线程
线程的生命周期
六种状态
新建状态New
Thread类
实现了Runnable接口
run方法,无返回值
Runnable接口
run方法,无返回值,通过Thread类或线程池来使用
Callable接口
作为FutureTask构造方法参数使用
call方法,有返回值,且可以抛出异常
call方法实际是在Runnable的run方法中被执行
就绪状态Runnable
调用线程的start()方法
不一定会立即运行,可能需要等待CPU分配时间片
阻塞状态Blocked
调用Object的wait方法后等待同步锁的状态
等待Waiting
发生在调用以下几个方法时:
不带参数的Object.wait()
不带参数的Thread.join()
LockSupport.park()
不带参数的Object.wait()
不带参数的Thread.join()
LockSupport.park()
超时等待 Timed-Waiting
与Waiting状态不同在于不会一直等待,而是等待指定的时间
发生在调用以下几个方法时:
Thread.sleep()
Object.wait(long timeout)
Thread.join(long timeout)
LockSupport.parkNanos()
LockSupport.parkUntil()
Thread.sleep()
Object.wait(long timeout)
Thread.join(long timeout)
LockSupport.parkNanos()
LockSupport.parkUntil()
终结状态Terminated
当线程运行完毕即死亡
Thread类的常用方法
Thread.sleep()方法
1、sleep方法是Thread类的静态方法,是为了保证该操作只对当前线程有效,避免线程安全问题,其它几个常用静态方法类似。
2、让当前正在运行的线程暂时停止运行,一段时间后会继续执行。
2、让当前正在运行的线程暂时停止运行,一段时间后会继续执行。
Thread.yield()方法
当前处于运行状态的线程主动放弃占用的CPU资源,转变为就绪状态,让其他先线程执行(让步)
join方法
在当前线程执行过程中引入另一个线程,并且当前线程需要等待另一个线程执行完毕后才能继续执行
Thread.currentThread()方法
获取当前正在运行的线程
停止线程的方法
Thread.stop方法:已废弃
使用一个标识来表示线程的状态,通过更改它的值来控制线程的运行和停止
interrupt 中断方法
让多个线程顺序执行的方式
利用Thread的join方法
线程间通信
wait方法
wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。
notify方法
notify()方法会唤醒一个等待当前对象的锁的线程(一般是高优先级的线程)开始排队
notifyAll方法
notifyAll()方法会唤醒其它所有等待当前对象的锁的线程
这三个方法用于协调多个线程对共享数据的存取(获取锁和释放锁),所以必须先获得锁(即在synchronized语句块内使用),否则会抛出IllegalMonitorStateException异常。
定时任务
Timer & TimerTask类
Timer:任务调度器,通过schedule(TimerTask task, long delay)等方法进行调度。
TimerTask:实现了Runnable接口。表示需要调度的任务,里面有一个run方法定义具体的任务。
TimerTask:实现了Runnable接口。表示需要调度的任务,里面有一个run方法定义具体的任务。
Timer缺点:内部是单线程,因此如果有异常产生,线程将退出,整个定时任务就会失败
线程池
ScheduledExecutorService,它是ExecutorService的子接口。弥补了Timer的缺陷
通过
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
和
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
等方法来实现定时任务调度
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
和
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
等方法来实现定时任务调度
异步任务
只需创建并启动一个线程执行该任务即可,或者使用线程池更好
并发编程
java.util.concurrent
线程池
作用
1、减少资源消耗。通过重复利用池中已创建的线程,减少频繁创建、销毁线程带来的资源消耗。
2、提高响应速度。当线程池中有空闲线程,任务到来时无需创建线程就能立即被执行。
3、提高线程的可管理性。由线程池对池中的线程进行统一的管理和监控,可以防止无限制创建线程造成的资源浪费。
2、提高响应速度。当线程池中有空闲线程,任务到来时无需创建线程就能立即被执行。
3、提高线程的可管理性。由线程池对池中的线程进行统一的管理和监控,可以防止无限制创建线程造成的资源浪费。
ExecutorService接口
ThreadPoolExecutor类
Executor的子类
Executors工具类
包含创建线程池的工厂(静态)方法
线程池大小配置
一般需要根据任务类型来配置线程池大小:
1、如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
2、如果是IO密集型任务,参考值可以设置为2*NCPU
1、如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
2、如果是IO密集型任务,参考值可以设置为2*NCPU
通过Runtime.getRuntime().availableProcessors()获得当前CPU个数
原子操作类,如AtomicInteger
使用CAS无锁技术保证变量的操作在多线程环境下正常,比synchronized控制的粒度更细、量级更轻,并且在多核处理器具有高性能
并发集合
ConcurrentHashMap
Java7引入分段锁技术Segment
Java8开始加入红黑树
CopyOnWriteArrayList
从字面上看就是”写时复制“。原理是当需要对集合中元素进行增删改操作时首先复制一个副本,对副本进行操作
适用于“读多写少”的并发场景
BlockingQueue
阻塞队列:
1)当队列为空时,获取元素的线程会等待队列变为非空;
2)当队列满时,存储元素的线程会等待队列可用。
内部通过Lock与Condition(即通过等待/通知机制)实现,通常用于生产者-消费者场景
1)当队列为空时,获取元素的线程会等待队列变为非空;
2)当队列满时,存储元素的线程会等待队列可用。
内部通过Lock与Condition(即通过等待/通知机制)实现,通常用于生产者-消费者场景
ArrayBlockingQueue:有界队列,即队列容量有限,内部是数组结构
LinkedBlockingQueue:既可以是有界队列,又可以是无界队列。若创建时不指定容量,则默认是Integer.MAX_VALUE
SynchronousQueue:同步队列
不保存元素
PriorityBlockingQueue:优先级队列
属于无界队列,按照队列中的任务优先级由高到低进行处理
通过Comparator比较器来决定优先级
ThreadLocal线程本地变量
1、它为每个线程都提供一个独立的变量副本,各个线程都可以改变自己的变量副本,各个线程间互不影响。因此可以解决并发问题。
2、实现的思路:
①在ThreadLocal类中有一个静态内部类ThreadLocalMap,用于存储每一个线程的变量副本,键为ThreadLocal对象,而值则是对应线程的变量副本;
②每个ThreadLocal实例都有一个唯一的threadLocalHashCode(这个值将会用于在ThreadLocalMap中找到ThreadLocal对应的value值)。
3、工作原理:
①在Thread类中维护一个ThreadLocalMap变量(与线程进行绑定);
②取值get操作:从ThreadLocal中取变量值时先获取当前线程,通过当前线程得到与之关联的ThreadLocalMap,然后再从ThreadLocalMap中根据当前ThreadLocal获取到其中的变量值(如果ThreadLocalMap为空,则返回初始化方法initialValue()的值);set操作类似。
2、实现的思路:
①在ThreadLocal类中有一个静态内部类ThreadLocalMap,用于存储每一个线程的变量副本,键为ThreadLocal对象,而值则是对应线程的变量副本;
②每个ThreadLocal实例都有一个唯一的threadLocalHashCode(这个值将会用于在ThreadLocalMap中找到ThreadLocal对应的value值)。
3、工作原理:
①在Thread类中维护一个ThreadLocalMap变量(与线程进行绑定);
②取值get操作:从ThreadLocal中取变量值时先获取当前线程,通过当前线程得到与之关联的ThreadLocalMap,然后再从ThreadLocalMap中根据当前ThreadLocal获取到其中的变量值(如果ThreadLocalMap为空,则返回初始化方法initialValue()的值);set操作类似。
CountDownLatch (倒计数器)
利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他几个任务执行完毕之后才能执行,就可以使用CountDownLatch来实现。
两个关键方法:await()等待其它线程执行完毕才开始执行、countDown()当有一个线程执行完毕就减1,当减到0时当前线程开始执行。
CyclicBarrier(回环栅栏)
通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。
关键方法:await()方法,此方法作用是等待其它线程都到同一状态时开始同时执行。类似于长跑比赛中,当所有运动员都准备完毕才开始比赛。
Fork/Join 多线程并行框架
利用分而治之的思想,将大任务分成小任务执行,然后合并结果;分别对应fork、join两个操作。
Fork/Join框架的核心是ForkJoinPool类,它是对AbstractExecutorService类的扩展。
ForkJoinPool实现了工作窃取(work-stealing)算法,并可以执行ForkJoinTask任务。
ForkJoinPool实现了工作窃取(work-stealing)算法,并可以执行ForkJoinTask任务。
信号量
线程同步
volatile
线程每次都从主内存中读取变量,改变后再写回到主内存。其作用如下:
1、使变量在多个线程间具有可见性;
2、禁止JVM进行指令重排序(保证有序性)。
1、使变量在多个线程间具有可见性;
2、禁止JVM进行指令重排序(保证有序性)。
不能保证原子性,非线程安全
应用场景
一写多读,保证变量可见性
开销较低的读写锁策略
只在写操作时需要加synchronized同步锁,读操作不需要
synchronized
在JVM层面实现了对临界资源的同步互斥访问,锁的释放不用人工干预,由虚拟机完成
1、在volatile基础上增加了互斥锁,所谓“互斥”就是同一时间只能有一个线程操作该资源;
2、在JDK1.5以后,为了弥补synchronized的不足,引入了Lock来代替它,将同步锁对象换成了Condition对象,并且Condition对象可以有多个。
2、在JDK1.5以后,为了弥补synchronized的不足,引入了Lock来代替它,将同步锁对象换成了Condition对象,并且Condition对象可以有多个。
用法
同步代码块
通常将外界资源作为锁的对象
synchronized(obj) {
// 同步操作代码
}
// 同步操作代码
}
用于保护外界资源不被多个线程并发修改
与同步方法比较而言,使用同步代码块的好处在于其他线程仍可以访问同步代码块以外的代码
同步方法
锁的对象是当前调用对象this
public synchronized void test() {
// 同步操作代码
}
// 同步操作代码
}
用于保护对象属性值不会被多个线程并发修改,因此需要保证调用方法的对象是同一个才有意义
同步静态方法
锁的对象是类的Class对象
public static synchronized void test() {
// 同步代码
}
// 同步代码
}
用于保护类的静态属性值不会被多个线程并发修改
缺点:
1、无法知道是否成功获取到锁
2、如果是多个线程需要同时进行读操作,一个线程读操作时其它线程只有等待(互斥)
1、无法知道是否成功获取到锁
2、如果是多个线程需要同时进行读操作,一个线程读操作时其它线程只有等待(互斥)
Lock接口
JDK层面实现的互斥锁,比起synchronized可控性更强,弥补了synchronized的不足。使用后必须手动释放锁,否则可能会导致死锁
包含lock(如果没获取到,会一直阻塞直到成功获取到锁)、tryLock(尝试获取锁,如果没获取到不会一直阻塞,可以指定等待时间)、unlock(释放锁)几个常用方法,都需要显示调用。
其实现类有:ReadLock(读锁)、WriteLock(写锁)、ReentrantLock(可重入锁)等
Condition接口
提供了类似Object类中wait、notify、notifyAll的方法,主要包括await、signal、signalAll方法,
与synchronized和wait、notify/notifyAll的搭配类似,这些方法与Lock锁配合使用也可以实现等待/通知机制
与synchronized和wait、notify/notifyAll的搭配类似,这些方法与Lock锁配合使用也可以实现等待/通知机制
无锁技术CAS
CAS即Compare And Swap的缩写。Java中的原子操作类如AtomicInteger底层就是依赖它实现的。
实现原理:CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
实现原理:CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
线程死锁
产生死锁的四个必要条件
互斥条件:资源不能被共享。即任一时刻一个资源只能给一个进程使用,其他进程只能等待,直到资源被占有者释放。
不可剥夺条件:已经分配的资源不能从相应的进程中被强制地剥夺,而只能由获得该资源的进程自愿释放。
请求和保持条件:已经得到资源的进程可以再次申请新的资源。
循环等待条件:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
不可剥夺条件:已经分配的资源不能从相应的进程中被强制地剥夺,而只能由获得该资源的进程自愿释放。
请求和保持条件:已经得到资源的进程可以再次申请新的资源。
循环等待条件:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
预防死锁的方法
合理对资源进行动态分配,以避免死锁
破坏死锁产生的四个必要条件
锁类别
公平锁
加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得
非公平锁
线程加锁时直接尝试获取锁,获取不到就自动到队尾等待
乐观锁
假设不会发生并发冲突,直接不加锁去完成某项更新,如果冲突就返回失败
悲观锁
假设一定会发生并发冲突,通过阻塞其他所有线程来保证数据的完整性
可重入锁
在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁
独享锁
锁一次只能被一个线程所持有
共享锁
锁可被多个线程所持有
ReadWriteLock,其读锁是共享锁,其写锁是独享锁
IO
区分阻塞、非阻塞、同步、异步
阻塞:等待结果返回,当前操作线程被挂起,后续操作被阻断
非阻塞:不等待返回结果,直接执行后续操作
非阻塞:不等待返回结果,直接执行后续操作
同步:轮询模型
异步:监听模型
异步:监听模型
序列化与反序列化
序列化
序列化类型
JDK Serializable 对象序列化
ObjectInputStream、ObjectOutputStream
JSON 序列化
Fastjson、Jackson、Gson等库
XML 序列化
XStream、JAXB等
序列化协议
反序列化
IO(同步阻塞IO)
字节流/字符流
对象序列化
实现Serializable接口
readObject(ObjectInputStream ois)、writeObject(ObjectOutputStream oos)方法
文件操作(磁盘读写)
创建/删除文件
读/写文件
NIO(同步非阻塞IO),利用多路复用技术
传统IO(BIO)与NIO的区别:IO是面向流的,NIO是面向缓冲区的
主要组成
Channel(通道,双向)
多路复用。传统IO的流是单向的,不能同时用来进行读写操作,而Channel则是双向的
通过open()静态方法打开一个通道
Selector(选择器)
Selector类是NIO的核心类,它能够检测多个注册的Channel上是否有事件发生,如果有事件发生,便获取事件然后进行相应的处理
通过Selector.open()静态方法选择一个事件进行处理
Buffer(缓冲区)
其作用相当于BIO编程中常用的基本类型数组(byte[]、char[]等),
比如ByteBuffer就是对byte数组进行的封装,使得操作起来更方便。
1、使用的是堆外内存,不受GC管理(GC主要是堆内存)
2、非线程安全
比如ByteBuffer就是对byte数组进行的封装,使得操作起来更方便。
1、使用的是堆外内存,不受GC管理(GC主要是堆内存)
2、非线程安全
Netty/Mina框架
基于NIO的网络框架,通常用于分布式应用开发中进行网络数据传输
AIO(异步非阻塞IO)
输入流/输出流
字节流/字符流
反射机制
涉及到的类或接口
Class(表示一个类或接口)、Constructor(构造方法)、Method(方法)、Field(对应接口或类中的属性)、
Modifier(修饰符private、protected、public、static、final等)、ParameterizedType(泛型参数类型)等
Modifier(修饰符private、protected、public、static、final等)、ParameterizedType(泛型参数类型)等
JavaBean的内省
针对JavaBean的反射,包括BeanInfo、PropertyDescriptor(属性描述器)、Introspector(工具类)等常用类或接口
泛型
jvm
内存模型
GC
classLoader
jvm调优
内存泄漏
内存使用后未得到及时释放,而且不能被GC回收,导致虚拟机不能再次使用该内存,此时这段内存就泄露了
内存溢出OOM
即OutOfMemoryError,当没有足够的空闲内存可供程序使用时出现
常见类型
Java Heap Space : Java堆内存溢出
此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。
对于内存泄露,需要通过内存监控软件(如heapdump)查找程序中的泄露代码;而堆大小可通过设置JVM的参数-Xms、-Xmx来解决。
对于内存泄露,需要通过内存监控软件(如heapdump)查找程序中的泄露代码;而堆大小可通过设置JVM的参数-Xms、-Xmx来解决。
PermGen Space:永久代内存溢出
即方法区内存溢出,如果程序加载的类过多,或者使用反射、gclib等这种动态代理生成类的技术,就可能导致该区发生内存溢出。
此种情况可以通过更改方法区的大小来解决,设置-XX:PermSize=64m -XX:MaxPermSize=256m参数。
另外,过多的常量尤其是字符串也会导致方法区溢出,因为常量池也位于方法区中。
此种情况可以通过更改方法区的大小来解决,设置-XX:PermSize=64m -XX:MaxPermSize=256m参数。
另外,过多的常量尤其是字符串也会导致方法区溢出,因为常量池也位于方法区中。
注:从Java8开始移除了Permgen Space,取而代之的是MetaSpace
StackOverFlowError:栈溢出
即虚拟机栈或本地方法栈区域内存溢出
出现原因:程序中出现死循环或递归次数过多;也可能是栈大小设置过小导致,可通过设置-Xss参数来调整栈大小。
监控工具
jps:查看Java进程
jmap:查看JVM当前的堆内存快照(heapdump)
jstack:查看JVM当前的线程快照,又称threaddump文件,它是JVM当前每一条线程正在执行的堆栈信息的集合
jinfo:实时查看JVM的参数信息
jstat:用于监控JVM的各种运行状态信息,如类的装载、内存、垃圾回收、JIT编译器等
jconsole:用于监控内存,线程、堆栈等信息
jprofile:类似于jconsole,比jconsole监控的信息更全面
常用指令
-Xms 1024M //表示堆内存初始化大小,默认是物理内存1/64;
-Xmx 1024M //表示堆内存最大值,默认是物理内存1/4;
-Xmn 512M //表示堆中新生代大小;
-XX:NewRadio //表示新生代与老年代的比例,-XX:NewRadio =2表示比例为1:2;
-XX:SurvivorRadio //表示新生代中eden区与survivor区的比例,默认比例为8;
-XX:MaxTenuringThreshold //表示经过多少次回收,对象进入老年代,默认是15;
-XX:PretenureSizeThreshold //直接进入老年代对象的值的阈值,-XX:PretenureSizeThreshold=3M,表示大于3M的对象可以直接 进入老年代
-XX:PermSize=512M //永久区初始大小
-XX:MaxPermSize=512M //永久区最大值
-XX:MetaspaceSize=100M //表示metaspace使用空间达到这个值时,执行垃圾回收
-XX:MaxMetaspaceSize=100M //metaspace的最大值
-XX:MaxDirectMemorySize //调整直接内存大小
-Xmx 1024M //表示堆内存最大值,默认是物理内存1/4;
-Xmn 512M //表示堆中新生代大小;
-XX:NewRadio //表示新生代与老年代的比例,-XX:NewRadio =2表示比例为1:2;
-XX:SurvivorRadio //表示新生代中eden区与survivor区的比例,默认比例为8;
-XX:MaxTenuringThreshold //表示经过多少次回收,对象进入老年代,默认是15;
-XX:PretenureSizeThreshold //直接进入老年代对象的值的阈值,-XX:PretenureSizeThreshold=3M,表示大于3M的对象可以直接 进入老年代
-XX:PermSize=512M //永久区初始大小
-XX:MaxPermSize=512M //永久区最大值
-XX:MetaspaceSize=100M //表示metaspace使用空间达到这个值时,执行垃圾回收
-XX:MaxMetaspaceSize=100M //metaspace的最大值
-XX:MaxDirectMemorySize //调整直接内存大小
应用层
spring
ConextLoaderListener监听器
是整个Spring应用启动的关键
Spring启动过程大致如下:
1、在Servlet容器启动后,会创建一个ServletContext(整个Web应用的上下文);
2、由于ContextLoaderListener实现了ServletContextListener,因此会在ServletContext创建完成后,其中的contextInitialized方法会自动被调用;
contextInitialized方法中将会通过ServletContext实例的getParameter()方法找到Spring配置文件位置,然后根据其中的内容为Spring创建一个根上下文(WebApplicationContext,即通常所说的IOC容器):
3、将WebApplicationContext作为ServletContext的一个属性放进去,名称是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
1、在Servlet容器启动后,会创建一个ServletContext(整个Web应用的上下文);
2、由于ContextLoaderListener实现了ServletContextListener,因此会在ServletContext创建完成后,其中的contextInitialized方法会自动被调用;
contextInitialized方法中将会通过ServletContext实例的getParameter()方法找到Spring配置文件位置,然后根据其中的内容为Spring创建一个根上下文(WebApplicationContext,即通常所说的IOC容器):
3、将WebApplicationContext作为ServletContext的一个属性放进去,名称是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
IOC
WebApplicationContext:即Spring IOC容器,由容器创建和管理Bean,使用时直接从容器中获取
配置方式
XML配置文件
@Resource/@Autowired注解配置
Bean的作用域
在Spring4中有以下几个作用域(见ConfigurableBeanFactory和WebApplicationContext接口源码)
(1)singleton:默认的作用域,仅为每个Bean对象创建一个实例。
(2)prototype:可以根据需要为每个Bean对象创建多个实例。
(3)request:为每个HTTP请求创建它自有的一个Bean实例,仅在Web相关的ApplicationContext中生效。
(4)session:为每个HTTP会话创建一个实例,仅在Web相关的ApplicationContext中生效。
(5)global session:为每个全局的HTTP会话创建一个实例。一般仅在porlet上下文中使用生效。同时仅在Web相关的ApplicationContext中生效。
(6)application:为每个ServletContext创建一个实例。仅在Web相关的ApplicationContext中生效。
(1)singleton:默认的作用域,仅为每个Bean对象创建一个实例。
(2)prototype:可以根据需要为每个Bean对象创建多个实例。
(3)request:为每个HTTP请求创建它自有的一个Bean实例,仅在Web相关的ApplicationContext中生效。
(4)session:为每个HTTP会话创建一个实例,仅在Web相关的ApplicationContext中生效。
(5)global session:为每个全局的HTTP会话创建一个实例。一般仅在porlet上下文中使用生效。同时仅在Web相关的ApplicationContext中生效。
(6)application:为每个ServletContext创建一个实例。仅在Web相关的ApplicationContext中生效。
Bean的生命周期
通过构造器或工厂方法创建 Bean 实例
为 Bean 的属性设置值和对其他 Bean 的引用
调用 Bean 的初始化方法
Bean 可以使用了
当容器关闭时, 调用 Bean 的销毁方法
为 Bean 的属性设置值和对其他 Bean 的引用
调用 Bean 的初始化方法
Bean 可以使用了
当容器关闭时, 调用 Bean 的销毁方法
依赖注入的几种方式
构造注入
属性注入setter
接口注入
ApplicationContext与BeanFactory
BeanFactory
是Spring里面最底层的接口,提供了最简单的容器的功能,负责读取bean配置文档,管理bean的加载与实例化,维护bean之间的依赖关系,负责bean的生命周期,但是无法支持spring的aop功能和web应用
延迟加载形式来注入Bean的
ApplicationContext
作为BeanFactory的派生,因而具有BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展
容器启动时,一次性创建了所有的Bean
AOP
动态代理
JDK动态代理(针对接口,代理类为其兄弟类)
目标对象实现了若干接口
CGLIB动态代理(针对类,代理类为其子类)
目标对象没有实现接口
AOP术语
切面
横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知
切面必须要完成的工作
前置通知(Before):在目标方法被调用之前通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
目标
被通知的对象
代理
向目标对象应用通知之后创建的对象
连接点
程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;
相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add();
方位为该方法执行前的位置
相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add();
方位为该方法执行前的位置
切点
每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
使用
@EnableAspectJAutoProxy 在配置类的类级别上启动自动代理功能
使用案例
事务管理
五大事务隔离级别
七大事务传播特性
事务管理器
核心接口是PlatformTransactionManager
org.springframework.jdbc.datasource.DataSourceTransactionManager:主要用于jdbc和mybatis数据源
org.springframework.orm.hibernate3.HibernateTransactionManager:主要用于Hibernate数据源
编程式事务
通过使用TransactionTemplate或者PlatformTransactionManager相关事务API来实现事务管理
声明式事务
XML配置<tx:advice>标签
@Transactional注解
异常回滚
只会回滚RuntimeException异常
实现方式
划分处理单元
AOP拦截需要进行事务处理的类
对事物处理实现(事务的生成、提交、回滚、挂起)
结合
Spring Boot
用于快速构建及开发Spring应用,可以看成是Spring框架的简化版
”约定优于配置“思想
使用约定的默认配置,减少配置文件编写
配置文件
properties文件
application.properties
yml文件
application.yml
基于注解配置
Spring原有的@Controller、@Service、@Component、@Bean、@Configuration等注解
@SpringBootApplication注解的类
表示整个应用的入口,通常位于项目的顶层包
properties配置文件读取
Spring原有的@Value
与@PropertySource配合使用读取指定配置属性值
缺点:
1、只支持单个属性值读取
2、只支持简单数据类型,如String、Boolean及数值类型
1、只支持单个属性值读取
2、只支持简单数据类型,如String、Boolean及数值类型
Spring Boot新增的@ConfigurationProperties
可以指定配置属性前缀,自动绑定属性值
比@Value功能更强大
1、支持实体属性自动封装;
2、支持复杂数据类型的解析,比如数组、列表List
1、支持实体属性自动封装;
2、支持复杂数据类型的解析,比如数组、列表List
starter简化Maven配置
预先定义好需要的依赖并打包,需要时直接引入即可
所有Spring Boot项目公共的父依赖
spring-boot-starter-parent模块
自动配置AutoConfigurer
为项目添加默认配置
application.properties/application.yml文件中定义的配置会覆盖默认配置
自带的spring-boot-autoconfigure模块
内置Servlet容器
Tomcat、Jetty、Undertow等
redis
Redis的安装及配置(Linux环境下)
下载源码
wget http://download.redis.io/releases/redis-2.8.13.tar.gz
解压后编译并安装
tar vxzf 下载的安装包名
cd 进入解压后的文件夹中
make 编译
make install 安装
redis.conf文件配置
后台启动配置参数
daemonize yes (no-不后台启动 yes-后台启动)
端口配置参数
port 6379 (可以更改自己的端口号,客户端登陆时,如改变默认端口,则需指定设置的端口进行登陆)
信息查看命令
查看redis客户端 which redis-cli
登录:redis-cli(默认登录本机6379端口)
info命令查看redis信息
登录:redis-cli(默认登录本机6379端口)
info命令查看redis信息
过期时间expire
EXPlRE <key> <ttl> 命令用于将键key 的生存时间设置为ttl 秒。
PEXPIRE <key> <ttl> 命令用于将键key 的生存时间设置为ttl 毫秒。
EXPIREAT <key> < timestamp> 命令用于将键key 的过期时间设置为timestamp所指定的秒数时间戳。
PEXPIREAT <key> < timestamp > 命令用于将键key 的过期时间设置为timestamp所指定的毫秒数时间戳。
PEXPIRE <key> <ttl> 命令用于将键key 的生存时间设置为ttl 毫秒。
EXPIREAT <key> < timestamp> 命令用于将键key 的过期时间设置为timestamp所指定的秒数时间戳。
PEXPIREAT <key> < timestamp > 命令用于将键key 的过期时间设置为timestamp所指定的毫秒数时间戳。
数据类型
String
最基本的存储结构
命令
get key
set key value
mget key1 key2...、
mset key1 key2...
setnx key value
incr/decr key
set key value
mget key1 key2...、
mset key1 key2...
setnx key value
incr/decr key
Hash
key-value结构
Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht
命令
hget key field
hset key field value
hmset key field1 value1 field2 value2...
hmget key field1 field2 ...
hset key field value
hmset key field1 value1 field2 value2...
hmget key field1 field2 ...
List
双向链表结构,数据按从左到右的顺序存储
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构
命令
lpush、rpush、lpop、rpop、lset key index value
Set
Set集合,不能有相同的值
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
命令
sadd key member1 member2...
scard key
set key index value
sismember key member
smembers key
scard key
set key index value
sismember key member
smembers key
zset(sorted set)
有序集合,按照score排序
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单
命令
zadd key score1 member1 score2 member2 ...
zcard key
zrange key start stop [withscores]
zcard key
zrange key start stop [withscores]
数据结构
ht(dict),raw,embstr,intset,sds,ziplist,quicklist,skiplist
存储
内存、磁盘、日志文件
持久化
RDB:把当前进程数据生成快照保存到硬盘的过程
触发机制
save命令:阻塞当前Redis服务器,直到完成为止,对内存较大的实例会造成长时间阻塞,线上不建议使用
bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束
(bgsave)流程说明
1.执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如果存在,bgsave命令直接返回
2.父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞
3.父进程fork完后,bgsave命令返回信息并不在阻塞父进程,可以继续响应其它命令
4.子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换
5.子进程发送信号给父进程表示完成,父进程更新统计信息
优缺点
1.RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间节点上的数据快照,适用于备份、全量复制等场景
2.Redis加载RDB恢复数据快于AOP方式
1.RDB方式数据没办法做到实时持久化/秒级持久化
2.RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,导致无法兼容
Append-only file(缩写为AOF)AOF:以独立日志方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的
管道pipeline
可以一次性发送多条命令并在执行完后一次性将结果返回
通过减少客户端与redis的通信次数来实现降低往返延时时间,而且其实现原理是队列,先进先出,因此可以保证数据的顺序性。
事务
redis事务是通过MULTI,EXEC,DISCARD和WATCH四个原语实现的
exec
MULTI命令用于开启一个事务,它总是返回OK。
MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
另一方面,通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务。
MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
另一方面,通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务。
WATCH
WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。
被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空多条批量回复(null multi-bulk reply)来表示事务已经失败。
被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空多条批量回复(null multi-bulk reply)来表示事务已经失败。
发布/订阅
Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能
redis架构模式
redis集群 Redis Cluster(3.0版本后官方提出)
哨兵 Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移
穿透优化
缓存空对象
存储层不命中后,仍将空对象保留到缓存层中
存在两个问题:空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间,比较有效的方法是针对这类数据设置较短的过期时间,让其自动剔除;缓存层和存储层数据会有一段时间的不一致,可通过消息系统清除掉缓存层中的空对象
存在两个问题:空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间,比较有效的方法是针对这类数据设置较短的过期时间,让其自动剔除;缓存层和存储层数据会有一段时间的不一致,可通过消息系统清除掉缓存层中的空对象
布隆过滤器拦截
在访问缓存层和存储层前,将存在的key用布隆过滤器提前保存起来,如果布隆过滤器查询不存在,那么就不会继续访问
缓存崩溃
1. 保证缓存层高可用
Redis Sentinel和Redis Cluster都实现了高可用
2. 依赖隔离组件为后端限流并降级
对重要资源(如Redis、MySQL、HBase、外部接口)进行隔离,让某种资源单独运行在自己的线程池中
3. 提前演练
项目上线前,模拟缓存层宕机后,应用及后端负载情况及可能出现的问题
键过期策略
定时删除:设置键的过期时间的同时,创建一个定时器
定期删除:每隔一段时间对Redis进行一次检查
惰性删除:每次获取键时,检查取得的键是否过期
内存淘汰机制
noeviction(默认):当内存使用达到阈值时,所有引起申请内存的命令会报错
allkeys-lru:主键空间中,优先移除最近未使用的
allkeys-random:主键空间中,随机移除
volatile-lru:设置了过期时间的键空间中,优先移除最近未使用的
volatile-random:设置了过期时间的键空间中,随机移除
volatile-ttl:设置了过期时间的键空间中,优先移除过期时间更早的
mongodb
doc
api
mapReduce
elasticSearch
index
modifyidex
indexsearch
netty
Netty基本概念
是什么
一个高性能、异步事件驱动的NIO框架
为什么
API使用简单,开发门槛低
功能强大,预置多种编解码功能,支持多种主流协议
定制能力强,可通过ChannelHandler对通讯框架灵活扩展
性能高
成熟、稳定,修复了已经发现的所有JDK NIO BUG
功能强大,预置多种编解码功能,支持多种主流协议
定制能力强,可通过ChannelHandler对通讯框架灵活扩展
性能高
成熟、稳定,修复了已经发现的所有JDK NIO BUG
怎么做
EventLoopGroup Bootstrap NioServerSocketChannel ChannelInitializer handler
为什么选择Netty及业务中使用Netty的场景
API使用简单,开发门槛低
功能强大,预置多种编解码功能,支持多种主流协议
定制能力强,可通过ChannelHandler对通讯框架灵活扩展
性能高
成熟、稳定,修复了已经发现的所有JDK NIO BUG
原生的 NIO 在 JDK 1.7 版本存在 epoll bug
为什么会TCP拆包黏包及解决方法
原因
应用程序写入的字节大小大于或小于套接口发送缓冲区大小
进行MSS大小的TCP分段
以太网帧的payload大于MTU进行IP分段
解决方法
消息定长,如每个报文大小固定长度为200字节,不够补空格
FixedLengthFrameDecoder
包尾增加回车换行符进行分割,如FTP协议
LineBasedFrameDecoder、StringDecoder 、DelimiterBasedFrameDecoder
将消息分为消息头和消息体,消息头包含表示消息(或消息体)总长度的字段,通常消息头的第一字段使用int32表示消息的总长度
更复杂的应用层协议
Netty线程模型
Reactor线程模型
Reactor单线程模型,多线程模型,主从多线程模型
NioEventLoop是Netty的Reactor线程
1. 作为服务端Acceptor线程,负责处理客户端的请求接入
2. 作为客户端Connecor线程,负责注册监听连接操作位,用于判断异步连接结果
3. 作为IO线程,监听网络读操作位,负责从SocketChannel中读取报文
4. 作为IO线程,负责向SocketChannel写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用于后续继续发送半包数据,直到数据全部发送完成
2. 作为客户端Connecor线程,负责注册监听连接操作位,用于判断异步连接结果
3. 作为IO线程,监听网络读操作位,负责从SocketChannel中读取报文
4. 作为IO线程,负责向SocketChannel写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用于后续继续发送半包数据,直到数据全部发送完成
Netty的零拷贝
接收和发送ByteBuffer采用DDIRECT BUFFERS
使用堆外直接内存进行socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统堆内存(HEAP BUFFERS)进行socket读写,JVM会将堆内存buffer拷贝一份到直接内存,然后写入socket
实现CompositeByteBuf
对外将多个ByteBuf封装成一个ByteBuf,对外提供统一封装后的ByteBuf接口,CompositeByteBuf实际是个ByteBuf的装饰器,将多个ByteBuf组合成一个集合
文件传输
文件传输类DefaultFileRegion通过transferTo方法将文件发送到目标Channel中,很多操作系统将文件缓冲区的内容发送到目标Channel中,而不需要通过循环拷贝的方式,提升了传输性能,降低了CPU和内存占用
Netty内部执行流程及重连实现
Netty原理
Zookeeper
选主过程
1.每个Server发出一个投票
由于是初始情况,每个服务器会将自己作为Leader服务器来进行投票,每次投票最基本的元素包括:所推举的服务器的myid和ZXID
2.每个服务器接收来自各方的投票,检查是否是本轮投票、是否来自LOOKING状态的服务器
3.处理投票
接收到其它服务器的投票后,针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
优先检查ZXID:ZXID较大的服务器优先作为Leader
如果ZXID相同,那么比较myid,myid较大的作为Leader
优先检查ZXID:ZXID较大的服务器优先作为Leader
如果ZXID相同,那么比较myid,myid较大的作为Leader
4.统计投票
每次投票后,服务器都会统计所有投票,判断是否已经有过半的机器接收到相同的投票
5.改变服务器状态
一旦确定了Leader,每个服务器会更新自己的状态:如果是Follower,那么就变更为FOLLOWERING,如果是Leader,就变更为LEADERING
集群间如何进行通讯
数据同步型
是指在Learner和Leader服务器进行数据同步的时候,网络通讯所用到的消息,通常有DIFF、TRUNC、SNAP和UPDODATE
服务器初始化型
是指在整个集群或新机器初始化时,Leader和Learner间相互通讯所使用的消息类型,常见的有OBSERVERINFO、FOLLOWERINFO、LEADERINFO、ACKEPOCH、NEWLEADER
请求处理型
是指在请求处理过程中,Leader和Learner间互相通讯所使用的消息,常见的有REQUEST、PROPOSAL、ACK、COMMIT、INFORM和SYNC
会话管理型
是指zk在进行会话管理的过程中,和Leader服务器间互相通讯所使用的消息,常见的有PING和REVALIDATE
节点加密方式
分布式锁的实现过程
排它锁
共享锁
羊群效应
锁改进
mybatis
activiti
activeMQ
子主题
设计模式
动态代理
工厂模式
抽象工厂
单例模式
责任链
actor模式
适配器模式
装饰模式
门面模式
策略模式
监听者模式
数据结构
线性表
顺序表
顺序表是指用一组地址连续的存储单元依次存储线性表的数据元素,通常指数组。
顺序表具有可随机存取的优点。
顺序表具有可随机存取的优点。
栈
栈,是限定仅在表尾进行插入或删除操作的线性表。后进先出(LIFO)。
队列
与栈相反,队列是一种先进先出(FIFO)的线性表
链队列
即为用链表表示的队列。有一个头指针和一个尾指针分别指向队列的队头和队尾。
循环队列
为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,
并称这种向量为循环向量。存储在其中的队列称为循环队列。
并称这种向量为循环向量。存储在其中的队列称为循环队列。
双端队列
双端队列(Double Ended Queue)是一种插入和删除操作均能在表的两端进行的队列
Java中的Deque接口
链表
单向链表
链表中的每个结点只包含一个指针,指向其后继结点。最后一个结点的指针为空
循环链表
链表中的最后一个结点的指针指向了头结点,因此整个链表形成一个环。解决了单链表的单向性问题
双向链表
在双向链表中,每个结点都有两个指针,分别指向其前驱结点和后继结点。同样解决了单链表的单向性问题
树
二叉树
分类
遍历
多叉树
子主题
DB
mysql
sql优化
唯一索引
组合索引
聚簇索引
表结构优化
存储过程
事务控制
引擎选择
分区分表
oracle
B+树
运维
配置分离
docker
nginx
linux
shell
架构层
SOA
服务分离
服务粒度
服务调度
dubbo
zookeep
springCloud
业务模型
基础模型
RPC
RMI
IO,socket
SOCET
WEBSERVICE
http
jms
跨进程访问
规范定义
异常处理
服务解耦
开发原则
可读性
可用性
开闭原则
单一原则
里氏替换
版本控制
maven
git
Gradle
0 条评论
下一页