java文档 持续更新

多线程

简介

	每一个程序都是一个进程,而每一个进程中都包含着多个线程,而如果一个进程中使用了多个线程来执行不同的操作,那么就是多线程,多线程存在的意义就是为了提高CPU的使用效率.

什么是线程安全

当多线程访问同一个类的情况下,这个类始终能表现出他正确的行为,那就说这个类是线程安全的

并发和并行

并发是指在单CPU情况下,多个进程同时执行,叫做并发,而并行是多CPU情况下,多个进程的同时执行,

线程的特点

一旦引入线程的概念,那么CPU调度的单位就是线程,而不是进程,每一个时间片上排队等待执行的就是线程,并且不同线程之间共用进程的资源

线程和进程的区别

线程和进程最主要的区别就是进程负责申请和分配资源,而线程负责具体的执行

四种多线程的实现

继承Thread类 
实现Runnable接口
实现Callable接口
通过FutureTask包装器来创建Thread线程

两种方式的区别 首先是Java中的单继承和多实现问题 其次重要的是实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享,并且代码和数据资源都是相互独立的,特别适合相同代码去处理同一个资源的情况,实现Runnable接口的这种方式使得线程.代码.资源三者有效分类,很好的体现了面向对象的思想

run()和start()

	run方法中是线程体,也就是线程需要执行的代码,而start是启动这个线程,需要注意的是,start方法调用后,线程并不会立刻执行,而是进入就绪状态,等待CPU的资源.一旦请求到CPU资源,线程就会正式执行,但是该线程并不会一直执行,而是执行完cpu给与的时间片,就会把cpu资源让出来,
	这其中涉及到了线程的生命周期问题,
	在new一个线程的时候,线程处于创建状态,创建的线程是一个空线程,系统并不会给线程分配资源,
	调用start方法的时候,线程处于就绪可执行状态,但是是可执行,并不是已经执行,而是等待cpu资源
	在调用sleep方法或者wait方法的时候,线程处于不可执行状态,在指定时间或者调用notify方法恢复
	当调用了stop方法的时候,或者抛出异常,或者run方法执行完成,线程就处于死亡状态
	
	创建状态-->就绪可运行状态--->运行状态或不可运行状态-->消亡状态

Runnable接口的使用

创建一个类A,实现Runnable接口,并且重写run方法,在run方法中添加具体逻辑
创建一个该A类的对象a,把a对象作为参数,创建一个Thread对象,调用Thread对象的start方法

Runnable如何做到资源共享

比如我们有一个类,类中有一个成员属性,该类实现了Runnable接口,当我们创建多个线程的时候,都把该类作为参数,那么多个线程都可以对这个属性进行操作,完成线程之间的资源共享

Thread常用的方法

getName();	--->获得当前的线程名称
setName();	--->设置当前线程的名称
getPriority(); --->获取当前线程的优先级
setPriority(); --->设置当前线程的优先级
currentThread(); --->获取当前线程对象
start();	--->让线程进入就绪状态

线程级别

最高级别为10,最低为1,级别可以取1-10中间的数字

线程阻塞

Join()
阻塞指定线程等到另一个线程执行完成,在继续执行
该方法必须在线程调用后使用才有效果

哪个线程调用了Join线程,那么当前线程就会被阻塞,直到调用了Join的线程执行完成,被阻塞的线程才会继续执行
sleep()
	调用sleep()的方法的线程,会进入睡眠状态,让出cpu资源给其他资源,但是它的监控状态依然保持,睡眠时间过后,会进入就绪状态,等待执行,需要注意的是,sleep是不会放开对象锁的,会抱着锁不放,进入睡眠状态

yield()

yield方法会结束当前运行的线程,并进入就绪状态,进入就绪状态后会立刻开始争夺cpu资源
setDaemon()
把调用该方法的线程作为后台线程,也就是守护线程或者寄生线程,必须放在start之前
Thread.setDaemon(false) 表示该线程为用户线程
Thread.setDaemon(true) 表示该线程为守护线程

所有的用户线程死亡后,守护线程就结束了;

sleep()和wait()方法的区别

	sleep方法是Thread类中的,而wait方法是object类中的,sleep方法被调用后,该线程抱着锁不放,进入睡眠状态,等睡眠时间过去,会被自动唤醒,进入就绪状态,等待执行,
	而wait方法被调用,线程会放开对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()后,本线程才会重新获取对象锁,进行运行状态

synchronized

	同步关键字,保证资源的安全或者方法的同步,我们可以把需要同步的代码放入代码块,并且加上synchronized关键字,就可以保证在一个线程执行完该代码块中的代码之前,不会被其他线程干扰
	或者直接在声明方法的时候加上synchronized关键字,监视器为this
	语法:
		synchronized(监视器){
          	...代码
		}
	注意:
		监视器必须是引用数据类型,不能为基本数据类型,可以使用包装类和String,但是极度不推荐
		在代码块中的代码中,不能改变该引用类型在内存中的引用
		一般使用共享资源作为监视器
		也可以专门建立一个对象,作为监视器,比如Object obj = new Object
		一旦确定某个对象作为监视器被锁住,那么其他所有使用这个监视器的都会被锁住

Lock

创建一个lock实现类对象,
在需要加锁的代码开始之前调用锁方法,lock.lock();
在需要代码执行完成后调用lock.unlock()解锁方法,
需要注意的是,必须保证lock.unlock()能被执行,也就是如果中间出现异常,也要能执行,所以最好放在finally里面

Lock和synchronized区别

Lock是一个类,而synchronized是一个关键字
Lock是乐观锁,而synchronized是悲观锁
lock必须要手动释放锁,如果发生异常,锁没有释放,就会造成死锁
synchronized是由JVM来释放锁的,如果发生异常,JVM会自动释放锁
虽然Lock在性能上比synchronized要好,但是在java1.7的时候,对synchronized进行了优化
所有推荐使用synchronized

线程之间的通信

主要通过object的wait()、notify() 和 notifyAll()方法,
wait()会放开锁,让出cpu的资源
notify()可以唤醒一个等待的线程
notifyAll()唤醒所有等待的线程

如何获得一个线程池

通过ThreadPoolExecutor类

线程池原理

当一个任务到线程池中
会先判断核心线程池是否满了,核心线程池如果不满,表示还有位置,那么就会立刻创建线程执行任务
如果核心线程池满了,会进行下一个判断,判断队列是否满了,如果不满,就会放入队列,等待核心线程空出位置
如果队列也已经满了,就会再进入下一个判断,判断线程池是否满了,如果连线程池也满了,就会按照设定的策略来处理无法执行的任务
如果线程没有满,就会创建线程来执行任务

线程池七个参数

核心线程数:线程池维护的最小线程数量,核心线程创建后不会被回收
(注意:设置allowCoreThreadTimeout=true后,空闲的核心线程超过存活时间也会被回收)。
最大线程数:当添加一个任务时,核心线程数已满,线程池还没达到最大线程数,并且没有空闲线程,工作队列已满的情况下,创建一个新线程并执行。
空闲线程存活时间:keepAliveTime  当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。
时间单位:unit 
        TimeUnit.NANOSECONDS
        TimeUnit.MICROSECONDS
        TimeUnit.MILLISECONDS // 毫秒
        TimeUnit.SECONDS
        TimeUnit.MINUTES
        TimeUnit.HOURS
        TimeUnit.DAYS
工作队列:workQueue  存放待执行任务的队列:当提交的任务数超过核心线程数大小后,再提交的任务就存放在工作队列,任务调度时再从队列中取出任务。它仅仅用来存放被execute()方法提交的Runnable任务。工作队列实现了BlockingQueue接口。

JDK默认的工作队列有五种:
ArrayBlockingQueue 数组型阻塞队列:数组结构,初始化时传入大小,有界,FIFO,使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥。
LinkedBlockingQueue 链表型阻塞队列:链表结构,默认初始化大小为Integer.MAX_VALUE,有界(近似无解),FIFO,使用两个重入锁分别控制元素的入队和出队,用Condition进行线程间的唤醒和等待。
SynchronousQueue 同步队列:容量为0,添加任务必须等待取出任务,这个队列相当于通道,不存储元素。
PriorityBlockingQueue 优先阻塞队列:无界,默认采用元素自然顺序升序排列。
DelayQueue 延时队列:无界,元素有过期时间,过期的元素才能被取出。
线程工厂: 创建线程的工厂,可以设定线程名、线程编号等。
拒绝策略:当线程池线程数已满,并且工作队列达到限制,新提交的任务使用拒绝策略处理。可以自定义拒绝策略,拒绝策略需要实现RejectedExecutionHandler接口。
JDK默认的拒绝策略有四种:

AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
DiscardPolicy:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态。
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
CallerRunsPolicy:由调用线程处理该任务。

方法上加锁,锁的是谁

静态方法锁的是该类的字节码文件
普通方法锁的是this

如何避免死锁

注意加锁的顺序
设置超时放弃
还有就是死锁检查,在多线程环境下,随机设置线程的优先级,随机让几个线程回退,剩下的线程保持他们自己的锁

volatile

所有线程都能看到共享内存的最新状态

atomicinteger

java中++i或者i++都是线程不安全的 使用atomicinteger可以提供对integer的线程安全操作

notify和notifyAll

notify是唤醒一个对象,而notifyAll是唤醒所有在等待的对象

为什么notify在object中

因为这些方法在操作同步线程的时候,需要知道该操作谁,也就是只有同一个锁上的等待线程,才能被同一个锁上的notify唤醒,不能出现唤醒其他锁中等待的线程

线程的三大特性

原子性 可见性 有序性

线程池有哪些

固定长度的线程池
可以缓存的线程池 长度可以增长
延时执行的线程池
单线程的线程池

IO流

分类

   io流从广义上来说分为两种,一种是节点流,一种是包装流,节点流处于io操作的第一线,而包装流负责对节点流进行包装,简化对节点流的操作

使用

使用IO流只需要遵循以下流程
1.创建一个输入流,给输入流指定数据源
2.创建一个中转站,比如一个byte数组,把读取到的内容存储到中转站
3.循环读取,设置到什么时候停下
4.创建一个输出流,并指定输出位置
5.使用输出流输出内容
6.关闭输出和输入流,如果使用了包装流,只需要关闭包装流,底层流也会关闭
节点流
InputStream
字节输入流 负责把数据从数据源中读取到内存中,按字节读取,中文会出现乱码
OutputStream
字节输出流,负责把内存中的数据写到目的地,一次写一个字节 创建是可以选择是覆盖还是追加 第一个参数是目的地(File),第二个参数是boolean true为追加 false为覆盖
Reader
字符输入流,负责把数据从数据源中读取到内存中,按字符读取,一次读取一个字符
Writer
字符输出流,负责把内存中的数据写到目的地
ByteArrayInputStream
字节数组输入流
ByteArrayOutputStream
字节数组输出流
包装流
BufferedInputStream
高速缓存输入字节流
	用于提高效率
BufferedOutputStream
高速缓存输出字节流
	用于提高效率
原理:会创建一个缓存区,缓存区大小8192,一次性读取8192字节,然后再一次性写入8192字节,减少硬盘读写次数,所以效率高.如果字节不够,读取完所有字节后,会自动刷新缓存区写入,并不一定要满8192字节才输出
关闭包装流 会自动关闭底层流
BufferedReader
高速缓存输入字符流
	提高效率

特殊方法:按行读取 readLine() 读取一行,返回一个字符串 读不到返回null,
BufferedWriter
高速缓存输出字符流
	提高效率
特殊方法:换行,每写一行可以换行 newLine()
转换流
InputStreamReader
		字节输入流转换成字符输入流
OutputStreamWriter
		字节输出流转换字符成输出流
字节流可以转换成字符流,字符流不能转换成字节流,因为没必要.把简单的弄成复杂的操作
对象流和数据流
DataInputStream
		读取使用数据流写出的文件
DataOutPutStream
		只能写基本数据类型和字符串
ObjectInputStream
		读取使用对象流写出的文件
ObjectOutPutStream
		可以写基本数据类型和字符串,还有对象
注意点: 用数据流写的只能用数据流读,同理用对象流写的也只能用对象流读
	   这四个流只有字节流,没有字符流.
	   写入到文件中,不是字符串,而是二进制
	   使用对象流写出的对象,一定要实现Serializable接口

集合

为什使用集合

1>数组长度不可变,集合可以扩容
2>数组存储数据结构单一,采取是线性表,集合采用多种数据结构,比如:链表,双端列表,哈希表等,比数组要灵活.
3>数组的length方法只会告诉你数组的容量,而不是数组的中存储数据的多少,而集合的size方法会告诉你存储了多少数据

	为什么使用集合,不使用数组?因为数组在被创建的时候就要指定初始长度,无法变化,而且数组操作的封装没有集合完善,并且数组是在内存中开辟的一块连续空间,添加和删除效率低,需要依次向前或者向后移动。

集合的架构

集合有两个顶级接口
一个是Collection 无序,且可重复
	Collection下有两个接口
		一个是List List存储的是有序,且值可以重复
		一个是Set  Set存储的是无序的,且值不能重复

一个是Map
	map存储的值键唯一,值可重复,且无序的

栈和队列

Stack 先进后出,先进去的后出来,push压栈,把一个元素压入栈顶,
pop:把栈顶的一个元素弹出,peek:获取栈顶的元素 已过时
队列
Queue 先进先出,删除只能进从头部进行,添加只能从尾部进入
双端队列(栈和队列)
Deque既可以当做栈来使用,也可以当做队列来使用,一般我们进行栈操作,都是使用这个对象,Deque是一个接口,我们一般使用它的子类,也就是LinkedList进行操作
Deque deque = new LinkedList();

List

ArrayList:
	底层采用的是可变的数组形式,数组默认长度为10,插入删除慢,而查询快
	每次扩容是原数组长度加上原数组长度向右移一位,也就是百分之五十
LinkedList:
	底层采用的是双向链表形式,插入删除快,而查询慢 比ArrayList多了几个方法,比如从插入到最前,和插入到最后,移除第一个值而移除最后的一个值

Vector和ArrayList的区别
Vector和ArrayList最主要的区别是:
	Vector是早期接口.而ArrayList是用于取代它的
	Vector是线程安全的,而ArrayList是着重于速度非线程安全的
	扩容的时候,Vector是扩容原长度的一倍,而ArrayList是50%

Set

使用哈希表结构来存储数据,那么一定要重写hashcode和equals方法
HashSet
	采用哈希表的方式,添加查询和删除都非常快,缺点是无序
linkedHashSet
	继承HashSet所有优点,并且他使用链表维护次序,也就是有序的
TreeSet
	唯一,有序,并且是按照内容大小排序的,如果使用TreeSet对自定类进行排序比较,自定义类必须实现Comparable接口并且泛型就是自定义类,并且重写compareTo方法

Map

LinkedHashMap
	哈希表+链表 有序 唯一
HashMap
	哈希表  key无序 key唯一 key和value 都可以为NULL
TreeMap
	二叉树 有序 key唯一
HashMap底层原理
在JDK1.7之前hashMap的底层是数组加链表,数组中的每个位置都是一个链表,JDK1.8之后新增红黑树
在put的时候,使用哈希函数来计算,得出插入的位置,这个位置是一个链表,如果该位置链表头结点为空,就插入该位置的第0位,如果有值,就会插入第1位,依次推下去,

在get的时候,也会使用哈希函数来计算,找到数组中链表的位置,然后再遍历这个链表,找到对应的值
HashMap和HashTable的区别
Hashtable是早期接口 HashMap是新接口
Hashtable是线程安全 而HashMap是线程不安全
Hashtable不允许null 而HashMap允许null值
Hashtable继承Dictionary类 而HashMap实现map接口
LinkedHashMap和HashMap
LinkedHashMap也是一个hashMap,但是其内部有一个双向链表,用于保持顺序 而hashMap是数组加链表
并且LinkedHashMap键值都不能为空
hashMap键值都允许为空,但是只能有一个键为空
HashMap的扩容为什么是2次方
之所以要是2的次方,是为了是indexFor方法中位运算代替取模运算 因为位运算会快于取模运算
HashMap在高并发的情况下,没有处理线程安全
	在put的时候会导致get无限循环 具体表现是cpu使用率会飙升
	因为在向里面put原始的时候,会检查容量是否足够,如果不足会扩容两倍,然后把数组从老的哈希表中迁移到新的哈希表,迁移过程就是一个rehash(瑞哈希)的过程,多线程同时操作就会形成循环链表
	
	元素丢失
	当多个线程同时执行addEntry(hash,key ,value,i)时,如果产生哈希碰撞,两个线程得到同样的存储位置,就会出现元素覆盖

集合的三种存储结构

集合有三种存储结构,速度从快到慢为
	哈希表
	二叉树
	线性表

线性表又分为链表和数组

AJAX

什么是ajax

ajax是一种异步交互技术,

为什么要使用ajax

	提升用户体验,因为传统同步情况下,用户必须等待后台数据返回,才能对网页进行操作,而ajax使用的异步机制,用户在等待后台返回数据的时候,依旧能对网页进行操作,而不会出现卡死的问题
	而且ajax引擎是在客户端运行的,可以减轻大用户量下对服务器的压力

ajax的最大特点是什么

局部刷新,而不是网页整个刷新,极大提高用户体验

你采用的是什么框架

Jquery

ajax的原理

创建一个xmlHttprequest异步调用对象,
创建一个http请求,并且设置目的地
设置响应http的请求状态变化的函数
发送http请求
获取返回数据,
调用javaScript和Dom实现局部刷新

如何解析JSON数据

通过JSON.parse()转换成JSON对象

跨域问题

用nginx的反向代理可以解决

异常

顶级类

Throwable

简单谈谈

顶级类下面分为错误和异常(Exception),异常分为两种,分别是检查时异常,一种是运行异常,检查时异常是指IO这种在编译时就强制要求处理的异常,而运行时异常是指数组越界,空指针,并发修改异常这些运行阶段会发生的异常

异常的最佳实践

方法尽量不要返回null

catch尽量对可能发生的异常进行处理

绝对不能把异常抛给客户端

Finally一定要关闭已经打开的资源

给出具体的异常信息,方便catch块对异常进行处理

如果涉及到事务,一定要注意在catch中再抛出一个异常,否则事务是不会回滚的,因为异常已经被处理

既然我们可以用RuntimeException来处理错误

既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常?

这是java设计决定的,大多数检查异常都在io包中,是肯定会出现请求的资源不存在的情况,一段健壮的代码必须能优雅的处理这种情况,所以java设计上就是让程序员考虑到这种情况,增加代码的强壮型

throw和throws

throws是声明本方法不处理异常,如果出现了异常,把异常交给调用者去处理
throw是抛出一个异常

异常链

指的是处理一个异常的时候,又抛出了另一个异常

你遇到过 OutOfMemoryError 错误嘛

内存泄露异常

怎么解决OutOfMemoryError

注意不要有太多静态对象 特别是静态集合
尽量多用局部变量,少用成员变量
如果一个对象不用了 要记得设置为null 方便GC回收
增加JVM的内存大小
还有就是算法方面的优化了

自定义异常

继承Exception类 

数据库

你了解哪些存储引擎

我所知道的存储引擎有:InnoDB,和Myisam isam heap BDB
他们之前的区别是:
	InnoDB支持事务,并且可以使用外键
	而Myisam并不支持事务,也没有外键,
	InnoDB不支持全文检索,而Myisam支持
	InnoDB支持行级锁,而Myisam支持表级锁
	InnoDB适合读少,写比较多,尤其是高并发的情况下,
	而Myisam适合读多,写比较少,对原子性要求不高,那么就使用Myisam
	

mysql的锁有哪些

行锁,innodb引擎的
表锁,Myisam引擎的
页锁:BDB引擎的

InnoDB行锁怎么实现的

基于索引来实现的 

Mysql的端口是什么

3306

ACID

既然说到了原子性,那么就要说一下数据库ACID了,ACID分别是原子性,一致性,隔离性,还有持久性
原子性:一组操作要么同时成功,要么同时失败
一致性:事务进行前和事务进行后,整体系统是稳定的,比如入账和出账的资金总额不会变
隔离性:确保各个事务直接不会互相影响,
持久性:一旦事务操作成功,那么对数据库的改变就是永久有效的

什么是分库分表

分表是为了解决单表情况下,数据过于庞大,查询缓慢的问题,
而分库是为了解决单台服务器性能无法支撑过大的查询量的问题.

那你们是怎么切分数据的?

切分数据有两种方式
水平切分和垂直切分
水平切分:
	依照表中数据的逻辑关系,同一张表的数据拆分到多台数据库上
垂直切分:
	按表切分,不同的表分到不同的数据库

Mysql数据库CPU飙升怎么解决

查看所有进程 show processlist  多秒状态没有变化的进程 全部干掉
查看超时日志,或者错误日志

Mysql的优化

主要可以从以下几方面入手
	sql语句的优化
		避免在where语句中使用!=操作符,和null值判断,避免使用in 用exists 用where替换havng
	索引的优化
		经常会变化的字段不要创建索引
	数据库结构的优化
		设计范式方面 和拆分表
	最后就是服务器硬件的优化

什么情况下MySql会锁行,什么情况下Mysql会锁表

当mysql使用的innoDB存储引擎,对一行数据进行操作,就会锁行,如果在执行一条语句的时候,需要全表扫描,那Mysql就会锁表

什么是读写分离

读写分离意思是把主数据库进行增删改操作,让从数据库进行查询操作,主要为了解写操作对查询操作效率的影响

那么你们怎么做读写分离

MySQL-Proxy 使用的异步复制,主服务器只管自己的修改操作,完成后发送二进制日志,并不关心从服务器是否接收到日志

索引有哪些

唯一索引
	不允许任何两行的具有相同的索引值
非唯一索引
	可以相同
主键索引
	主键天生就是索引
聚集索引
	行的物理顺序和键值的逻辑索引顺序相同

heap表

mysql的数据引擎,内存表,一般用来做缓存

Mysql和Oracle

开源与不开源的问题,也就是安全性的问题

MySql enum字段

使用enum可以设置字段的区间范围,让数据库很好的控制字段的值,但是数据迁移就不可能被其他数据库支持了

数据库用存货币用哪种格式

Decimal和Numric

MYSQL数据表损坏的原因

mysql没有正常关闭,比如断电,进程被杀死,磁盘故障等等
修复的话 就只有采用备份表了

SQL优化

不要在where子句中进行null值判断
不要在where子句中进行!=判断
不要在where子句中进行in和not in
不要使用like和where子句中对字段进行表达式操作函数操作
这些都会使数据库放弃索引,

悲观锁

简介:
	悲观锁如它的名字一样,以一种悲观的状态去看待数据库的读写,就是总会觉得有人和他抢着读写数据,所以通过加上读锁或者写锁的方式来防止别人来修改或者读取自己正在读写的数据,是通过设置数据库实现
读锁(共享锁):
	在读取数据过程中,自己不修改,也不希望别人修改,我们就可以加入读锁,需要注意的是,读锁只有一个,多个用户持有的读锁都是同一个,如果读锁只有一个用户持有,这个用户可以对数据进行修改,而读锁被多个用户持有,则这些用户都不能进行修改
写锁(排他锁):
	用户不仅要读,还要写,并且在加上写锁后,其他用户不仅仅不能写,还不能读.直到这个用户放开这个锁,其他用户才能进行读或者写
SQL语句实现:
读锁(共享锁):
	select * from table lock in share mode

写锁(排他锁):
	select * from table for update
Hibernate实现:
通过调用get重写方法 也就是三个参数的get方法来实现
session.get(字节码对象,ID,锁)

LockOptions.READ(读锁)
LockOptions.UPGRADE(写锁)

乐观锁

简介:
并不会通过设置数据库来强制锁表,而是通过制定一套规则,让并发读写按照规则和顺序进行
原理:
我们通过在表中加入一个对实体和逻辑毫无意义的Integer字段version,原始值为1,并且加入一套逻辑,也就是制定一套规则
当并发访问的时候,只要数据变动一次,version就加一,后面插入的数据version必须必前面插入的数据的version要高,才能插入,否则驳回插入,并且version加一后再尝试插入,

比如当A和B同时对一条数据进行修改 数据库现在的version版本是1
当A修改数据的时候,会把数据库的数据取出,进行修改的同时version也会被加1 修改完成准备提交
而B也在修改数据,也会把数据库的数据取出,进行修改的同时version版本也会加1 修改完成准备提交
当双方都提交的时候,会有一方比另一方快,当有一方的数据写入数据库 数据库version原本为1 写入完成后就会变成2 此时一方已完成操作,另一方也写入的时候,版本并没有大于数据库中的版本,就会被驳回 当被驳回的时候,被驳回方会从数据库把数据再取出来 在最新的数据上进行修改操作 同时version会加1,然后再写入数据库

Hibernate实现乐观锁
给要实现乐观锁的实体加上一个私有Integer成员属性version 初始为1
同时给实体对应的Hibernate配置文件中加入<version name = "version"></version>
注意version标签要在id标签之后,property标签之前

JVM

内存划分

堆 非堆 离堆

堆区中 又有年老代和年轻代 年轻代中有伊甸区,幸存一区,幸存二区

非堆区 永久代

离堆  JVM之外的内存

内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTJyI6xd-1655798386709)(/Users/admin/Desktop/%E9%9D%A2%E8%AF%95/%E5%91%A8%E5%A4%A7%E4%BD%AC,%E5%86%8D%E8%A7%81%E4%BA%86/%E9%9D%A2%E8%AF%95%E5%87%86%E5%A4%87/JVM/%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E5%9B%BE.png)]

垃圾回收机制

JVM中把对象分成三种:年轻代,年老代,永久代,
年轻代:
	被new出来的对象,年轻代区域中又分为伊甸区,幸存者一区,幸存者二区
年老代:
	经历了多次回收还没被回收的对象就会进入年老代
永久代:
	用于存放一些静态的方法或者成员

对象何时被回收

	当一个对象的引用计数器为0的时候,或者这个对象符合不可达的原则,不能通过其他任何对象到达这个对象,这个对象就会被标记成可以回收的对象,等待GC运行,就会被回收
	一般来说,伊甸区满了,就会信息mino GC(米诺),而从新生代升级到老年代,老年代空间不够的话,就会进行fullGC

完整的GC过程

对象在被创建在新生代,如果新生代内存空间不够,进行MGC新生代回收,对象每经过一次MGC没有被回收,年龄就会+1,年龄到达15,就会被送入年老代,而大对象直接会被送入年老代,大对象就是需要在内存中开辟连续空间的

双亲委派?

先交给父类加载,父类再交给父类的父类,递归到最顶层,顶层尝试加载,无法加载交给子类加载,子类无法加载交给子类的子类,依次递归,保证类只会被加载一次

老年代和新生代清理规则

新生代使用的是复制清理,而老年代是标记清理
标记清理的缺陷是应用程序会停止,而复制清理的确定是需要额外的内存空间

垃圾回收器有哪几种

串行收集器 新生代和老年代
并行搜集器 新生代和老年代
CMS老年代并行回收器
G1回收器

串行收集器的是面向单线程环境的,这个收集器工作的时候,会冻结所有应用线程
并行搜集器是默认JVM搜集器,它采用的多线程来扫描和压缩堆,缺点就是也会暂停应用线程
CMS采用的就是标记算法,标记那些要回收的对象,但是在算法在运行的时候,如果对象的状态改不了,它又要从新确认自己标记的对象是真的要被回收的,这个收集器的缺点就是比较耗费cpu资源
G1回收器在jdk7的时候引入的,它会把堆分成多个区域,使用多线程来扫描他们,并且会优先扫描垃圾比较多的区域

大部分是选择默认的JVM的收集器,少部分主动选择垃圾回收器 会在CMS和G1中选择一个

JVM新生代和老年代的比例

默认是1比4

JVM调优

从这几方面入手
年轻代和年老代的划分是否合理
年轻代的区域尽量设置大一点
垃圾回收算法设置是否合理
垃圾回收器的选择

谈谈Mybatis和Hibernate

	Hibernate和Mybatis都是持久层的框架,也都是ORM映射框架,Hibernate是完全的ORM映射框架,它高度封装sql语句和表关系,只要配置好,就能让程序员无需关注sql语句的编写,只需要关注业务逻辑即可
	而Mybatis并不是完全的ORM映射框架,它只是对JDBC的封装,sql语句和表关系都要自己去写,因此灵活性高,也能对sql进行优化,获得更高的性能
	Hibernate由于是高度封装的,无法对sql语句进行优化,因此比较适合传统项目,开发快
	但是对于高并发的互联网项目来说,Mybatis的灵活性和sql优化带来的效率,是更优于Hibernate的

resultType和resultMap的区别

resultType只有查询出来的列名和实体中的属性名一致,该列才能映射成功
resultMap如果查出来的列名和属性名不一致,通过定义一个resultMap来完成映射

Mybatis中$和#的区别

#{}会对传入的参数进行处理,将参数变成字符串
${}是不会进行任何处理的,
一般使用#{},在sql进行预编译的时候,#{}是占位符,而${}直接就会参与sql预编译,会引起sql注入问题

Mapper中的方法和DAO接口方法是怎么绑定到一起的

在启动的时候,配置文件加载完成,Mybatis会根据xml 去自动使用动态代理生成接口的实现类

MyBatis和Hibernate的区别

 1. hibernate是全自动,而mybatis是半自动
hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。

2. hibernate数据库移植性远大于mybatis
hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库(oracle、mysql等)的耦合性,而mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。

3. hibernate拥有完整的日志系统,mybatis则欠缺一些
hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而mybatis则除了基本记录功能外,功能薄弱很多。

4. mybatis相比hibernate需要关心很多细节
hibernate配置要比mybatis复杂的多,学习成本也比mybatis高。但也正因为mybatis使用简单,才导致它要比hibernate关心很多技术细节。mybatis由于不用考虑很多细节,开发模式上与传统jdbc区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期bug较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate则正好与之相反。但是如果使用hibernate很熟练的话,实际上开发效率丝毫不差于甚至超越mybatis。

5. sql直接优化上,mybatis要比hibernate方便很多
由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;虽有hql,但功能还是不及sql强大,见到报表等变态需求时,hql也歇菜,也就是说hql是有局限的;hibernate虽然也支持原生sql,但开发模式上却与orm不同,需要转换思维,因此使用上不是非常方便。总之写sql的灵活度上hibernate不及mybatis。

谈谈Spring

什么是spring

	spring是一个开源的框架,它的核心就是提供了一种新的机制来管理对象和依赖关系,这两点体现在控制反转和依赖注入,
	Spring的底层是通过demo4j来解析xml文件,使用反射来实例化对象,

什么是控制反转和依赖注入

	依赖注入是说在一个对象被创建的时候,把其依赖的类注入给它,这就是依赖注入,而控制反转则是把对象的创建权交给Spring来管理,这两点都很好的解决了耦合度问题

什么是AOP

	AOP的意思就是面向切面编程,简单来说就是纵向代码的横向抽取,负责来说就是某些情况下,我们很多地方都需要使用到某些代码,这些代码我们把它称之为通知,而需要这些代码的地方,我们叫它们切点,通知和切点组合就是切面,而我们把切面应用到对象上这个过程就叫织入,织入完成后产生的类,我们就把它称之为aop代理,SpringAOP代理有两种,根据被代理的类,也就是目标对象是否有接口来选择不同的代理类,有接口就使用jdk的动态代理,没有接口就选择cglib代理

Spring使用了哪几种设计模式

工厂模式:根据id 反射获取对象
单例模式:默认创建的对象就是单例的
代理模式:aop使用的jdk动态代理和cglib代理
策略模式:在aop实现中 根据类有没有实现接口 而选择不同的代理

Spring默认是单例还是多例

单例 通过scope可以设置多例

Spring是如何管理Bean的

通过BeanFactory或ApplicationContext管理,BeanFactory通过工厂模式来初始化bean 而ApplicationContext是他的子类,不但能管理bean还能提供aop,事务和国际化支持

Spring事务的传播行为

PROPAGATION_REQUIRED
如果有事务,沿用这个事务,如果没事务,开启事务

Spring怎么保证线程安全

通过设置多例,或者把对象绑定到ThreadLocal中,从而达到隔离对象,保证线程安全

Spring五个事务隔离级别和七个事务传播行为

spring事务:
什么是事务:
事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败.

事务特性(4种):
原子性 (atomicity):强调事务的不可分割.
一致性 (consistency):事务的执行的前后数据的完整性保持一致.
隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰
持久性(durability) :事务一旦结束,数据就持久到数据库

如果不考虑隔离性引发安全性问题:
脏读 :一个事务读到了另一个事务的未提交的数据
不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致.
虚幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致.

解决读问题: 设置事务隔离级别(5种)
DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

未提交读(read uncommited)脏读,不可重复读,虚读都有可能发生
已提交读 (read commited)避免脏读。但是不可重复读和虚读有可能发生
可重复读 (repeatable read)避免脏读和不可重复读.但是虚读有可能发生.
串行化的 (serializable)避免以上所有读问题.

Mysql 默认:可重复读
Oracle 默认:读已提交

这里写图片描述
read uncommited:是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
read commited:保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。
repeatable read:这种事务隔离级别可以防止脏读,不可重复读。但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了以下情况产生(不可重复读)。
serializable:这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读之外,还避免了幻象读(避免三种)。

事务的传播行为

保证同一个事务中
PROPAGATION_REQUIRED支持当前事务,如果不存在 就新建一个(默认)
PROPAGATION_SUPPORTS支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY支持当前事务,如果不存在,抛出异常
保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED如果当前事务存在,则嵌套事务执行

Spring配置声明式事务

\* 配置DataSource
\* 配置事务管理器
\* 事务的传播特性
\* 那些类那些方法使用事务

Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问 时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

根据代理机制的不同,Spring事务的配置又有几种不同的方式:

第一种方式:每个Bean都有一个代理

第二种方式:所有Bean共享一个代理基类

第三种方式:使用拦截器

第四种方式:使用tx标签配置的拦截器

第五种方式:全注解
1、spring事务控制放在service层,在service方法中一个方法调用service中的另一个方法,默认开启几个事务?

spring的事务传播方式默认是PROPAGATION_REQUIRED,判断当前是否已开启一个新事务,有则加入当前事务,否则新开一个事务(如果没有就开启一个新事务),所以答案是开启了一个事务。

2、spring 什么情况下进行事务回滚?

Spring、EJB的声明式事务默认情况下都是在抛出unchecked exception后才会触发事务的回滚

unchecked异常,即运行时异常runntimeException 回滚事务;

checked异常,即Exception可try{}捕获的不会回滚.当然也可配置spring参数让其回滚.

spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(Spring默认取决于是否抛出runtime异常).
如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。

谈谈SpringMVC

什么是SpringMVC

SpringMVC是基于MVC的一个框架,用于接收客户端的请求,并对其进行解析

SpringMVC是怎么工作的

用户的请求发送到前端控制器,
前端控制器会调用处理器映射器,
处理器映射器根据配置或者注解来找到最终要执行处理器,并且生成处理器对象和处理器拦截器返回给前端控制器
前端控制器会调用处理器适配器
处理器适配器会调用具体的处理器
处理器会执行返回,返回模型和视图
处理器适配器把模型视图交给前端控制器,
前端控制器会再把模型视图转交给视图解析器
视图解析器解析后返回具体视图给前端控制器
前端控制器会根据返回的视图,进行渲染,并且返回给客户端

SpringMVC的控制器是不是单例

是单例的,
它是通过方法的多例来解决并发问题,每当有一个请求访问某个方法,都会创建一个该方法的副本,所以不会出现线程安全问题

SpringMVC有哪些注解

@RequestMapping:映射地址路径
@RequestParam:绑定单个请求参数值 主要用来解决前台和后台参数名不一致
@PathVariable:把URL中的变量映射到参数上
@CookieValue:获取cookie值
@ResponseBody:指定返回格式 配合转换器一起使用

SpringMVC的拦截器怎么写

继承适配器类,然后再配置文件中配置 或者实现HandlerInterceptor接口

SpringMVC和Struts2的区别

1.springmvc入口是一个servlet前端控制器(DispatcherServlet),struts2入口是一filter过滤器(StrutsPrepareAndExecuteFilter).

2.struts2通过在action类中定义成员变量接收参数,(属性驱动和模型驱动),它只能使用多例模式管理action.springmvc通过在controller方法中定义形参接收参数,springmvc可以使用单例模式管理controller.      

3.springmvc是基于方法开发的,注解开发中使用requestMapping将url和方法进行映射,如果根据url找到controller类的方法生成一个handler处理器对象(只包括一个method).
struts2是基于类开发的,每个请求过来创建一个action实例,实例对象中有若干个方法.
开发中建议使用springmvc,springmvc方法更类似service业务方法.

4.struts2采用值栈存储请求和相应的数据,通过OGNL存取数据,springmvc通过参数绑定期将request请求内容解析,并给方法形参赋值.

5.struts2和springmvc的速度是相当的,由于struts2的漏洞较多,跟多企业使用springmvc

分布式

分布式事务的解决

使用两段式或者三段式提交

两段式提交:
  第一阶段:
	1.事务协调者询问所有参与者,是否可以进行事务,如果全部就绪,进入下一阶段
	2.各个事务参与者执行自己的事务,并且记录信息到事务日志
	3.所有参与者把事务的执行结果告诉协调者
  第二阶段:
	1.如果所有参与者都告诉协调者事务全部成功了,协调者会通知所有参与者进行事务提交
	2.参与者完成事务提交后,会把结果返回给协调者,
	3.完成事务
	以上步骤任何一个协调者发出了回滚请求,就会回滚事务
缺点是如果协调者出了问题,只给一部分事务参与者发送了事务,而没给其他参与者发送就会出现脑裂

分布式锁怎么设计

使用zookeeper的curator框架可以解决分布式锁

dubbox有哪些组件

网络通信框架
RPC远程调用框架 
Registy 用于注册服务和服务的发布订阅

负载均衡算法有哪些

轮询
	顺序循环的发到每个服务器 
加权比率
	根据权重比例来分配
最少连接
	优先把请求给最少的服务器
最快响应
	优先把请求给最快响应的服务器

分布式锁

	分布式锁是指在分布式环境下,多个节点并发访问共享资源的一种锁实现,为了保证多系统多节点共享某个资源引发的不安全性
	可以使用zookeeper的顺序一致性来解决

分布式事务

	分布式事务是指一组操作不在同一个主机上,如何保证不同主机要不同时成功,要么同时失败

分布式锁详解

Redis:
	在获取锁的时候使用redis命令setnx上锁,并且使用expire命令为这个锁添加一个过期时间,超过了这个时间,就自动释放锁,防止死锁的发生,
	锁的值为一个随机生成的uuid,通过这个uuid在释放锁的时候,进行判断
	获取锁的时候设置一个时间,如果超过这个时间,则放弃获得锁,防止一直在获取锁
	释放锁的时候通过uuid来进行判断,是不是该锁,如果是,就执行delete进行释放锁
	
zookeeper:
	在zookeeper中创建一个节点,比如取名叫做lock,客户端想获得锁的时候,就会再lock下创建一个新的子节点,子节点被创建出来后,会遍历一边lock下的所有子节点,确定最小的子节点,如果自己就是最小的子节点,那么自己就获取锁,执行任务,完成删除自己这个节点,如果自己不是最小的子节点,那么他就会根据自己的大小进行排队等待,监听lock的节点变化,每当lock下面的节点被删除的时候,都会进行一次遍历判断,只到轮到自己是最小的节点,然后执行任务,完成后删除节点.
	这种方式是基于zookeeper的有序性来完成的,每当有一个事务操作到达zookeeper,zookeeper都会对这些事务按照来到的先后顺序进行自增排序,确保有序的执行.
	不过一般来说不会使用zookeeper的原生API进行操作,会使用curator框架来做
	
两者区别
       Redis分布式锁,必须使用者自己间隔时间轮询去尝试加锁,当锁被释放后,存在多线程去争抢锁,并且可能每次间隔时间去尝试锁的时候,都不成功,对性能浪费很大。
       Zookeeper分布式锁,首先创建加锁标志文件,如果需要等待其他锁,则添加监听后等待通知或者超时,当有锁释放,无须争抢,按照节点顺序,依次通知使用者。

分布式事务详解

消息中间件异步方式:
	这种方式中有这么几个角色:MQ发送方,MQ服务器,MQ订阅方
	首先,MQ发送方发送事务消息给MQ服务器,告诉MQ服务器,我要准备要提交XX事务了,你准备好告诉订阅方,
	注意!此处是MQ服务器准备好告诉MQ订阅方,但是还没有告诉MQ订阅方
	MQ服务器给与响应,告诉MQ发送方,我已经收到了事务消息,你提交吧,
	MQ发送方确定MQ服务器收到了事务消息后,就会开始提交事务;
	事务提交成功后,MQ发送方会发送事务提交成功消息给MQ服务器,说我已经提交成功了,
	MQ服务器收到事务提交成功后,就会把第一步中存储的消息告诉MQ订阅方,MQ订阅方收到消息后也开始提交自己的事务;
	如果MQ发送方的事务提交失败了,也会发送消息给MQ服务器,说我的事务提交失败了,你把我第一步给你的事务消息丢弃掉,不要发送给MQ订阅方
	这样就还无法保证事务的一起成功或者失败,因为无法保证MQ订阅方的事务成功,所有我们还要加上一层保险
	在第一步的时候,MQ发送方在发送事务消息的时候,就把对应的事务消息存到表中
	MQ订阅方提交事物的结果要返回给MQ服务器,MQ服务器再告诉MQ发送方,MQ发送方会根据MQ订阅方的事务提交结果,来进行处理,如果MQ订阅方说自己事务提交失败,那么MQ发送方会把表中的事务消息取出来,从新发送,一直到成功为止;
	
	两段式提交:
		参照其他文档,其他文档中有,两段式提交有很多问题,没有异步确保的方式好
		
ps:我已经尽量的通俗化了...如果没有理解,千万不要在面试的时候提起这个,除非对方问了,那么就硬着头皮说吧

分布式Session同步问题

采用粘性session或者强制同步方式,最常用redis代替session

粘性session:
	当某个客户端请求第一次发送到服务器,服务器基于负载均衡,把这个请求交给A服务器,在A服务器创建一个session,并且绑定该客户端,在一次会话当中,第二次请求发送过来,直接把请求发送给A服务器,相当于绑定,当会话结束后,解除绑定
	
强制同步:
	强制把session的数据同步到服务器所有session中,不管是否需要
redis:
	使用redis代替session,设置一个过期时间,模拟session

Dubbox

Dubbox是什么

提供RPC远程服务调用和SOA服务治理的框架,RPC是一种协议,而soa是面向服务的架构

Dubbox使用哪种协议

默认的Dubbox协议

你们使用的哪种注册中心

zookeeper注册中心

Dubbox的服务调用是阻塞的么

默认是阻塞的,可以异步调用,不过没有返回值

Dubbox的通信框架

我们使用的是netty

Dubbox默认用什么序列化

默认使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。

Dubbox负载均衡怎么配置

Dubbox提供了多种负载均衡的策略,比如轮询,随机和最少活跃等,默认使用的随机调用
轮询:
	按照权重依次轮替 容易卡死 如果某个请求在某台机器卡死了 其他请求到了这台机器也会一起卡死
随机:
	按权重来随机
最少活跃:
	处理请求越慢的收到的请求越少,处理越快的收到请求越多
hash一致性:
    一致性Hash,相同参数的请求总是发到同一提供者。
    当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
    算法参见:http://en.wikipedia.org/wiki/Consistent_hashing。
    缺省只对第一个参数Hash,如果要修改,请配置<dubbo:parameter key="hash.arguments" value="0,1" />
    缺省用160份虚拟节点,如果要修改,请配置<dubbo:parameter key="hash.nodes" value="320" />

注册中心怎么配

使用dubbox:registry标签来配置

Dubbox连接注册中心和直连

开发测试用直接连接 上线用注册中心

Dubbox的安全机制

通过Token令牌来防止用户绕过注册中心

zookeeper集群挂了

发布者订阅者还是能通信,在Dubbox启动时候,消费者会从注册中心获取生产者的地址接口等数据,缓存到本地

dubbox集群容错方案

失败自动切换
快速失败,只发起一次调用,失败立即报错
失败安全,出现异常,直接忽略
失败自动恢复,定时重发
并行调用多个服务器,只要一个成功立即返回
广播调用所有提供者,逐个调用,任意一台报错则报错

服务上线怎么不影响旧版本

生成环境和备份环境互为备份,直接切换

Zookeeper

简介

	Zookeeper是一个基于观察者设计模式的分布式管理框架,他负责存储和管理大家都关系的数据,然后接受观察者的注册,一旦这些数据发生改变,Zookeeper就将负责通知已经在Zookeeper上注册的观察者做出对应的反应,从而实现集群中主仆管理模式
	Zookeeper专门用于解决分布式问题的协调服务框架,基于ZBK算法协议,部署至少需要三个节点,推荐节点为奇数,这些节点会基于主从算法,从所有节点中选举出一个主节点,其他为父节点,如果主节点挂掉了,会再从新选举一个节点作为主节点,只要有半数以上节点正常运行,Zookeeper就能正常对外服务

原子性

	如果某个web客户端对Zookeeper某一个节点的某个数据进行更新操作,那么在该客户端完成操作之前,其他客户端都无法对该数据进行操作,直到该客户端完成操作,然后Zookeeper将该修改数据同步到所有节点上,其他客户端才能继续操作这个数据,可以把它理解成锁
	加锁这个就是通过ZAB协议完成的
	而节点同步通过的就是Paxos算法
	原子性说的就是只要有一个节点应用了某个事物,其他所有节点都会被应用,反过来也是一致的,绝对不会出现一部分应用了,一部分没有应用

顺序一致性

	从一个客户端发起的事物请求,最终都会严格的按照发起的顺序,依次应用到Zookeeper中,其原理是Zookeeper会按照顺序给这些请求分配一个递增的编号,这个编号决定执行的顺序

单一视图

无论客户端连接的是哪一个Zookeeper服务器,看到的数据都是一致的,也就是同一样的数据

可靠性

	一旦客户端的某个事物请求被Zookeeper应用,并且同步到所有节点,那么该事务引起的对数据库的修改就一定会被保存下来,除非有其他事务对这个操作进行了修改

实时性

一旦所有节点同步完成,那么客户端访问就能立刻看到最新的数据

Curator框架

Curator能完美实现zookeeper的分布式功能

ZAB协议

	ZAB协议是主节点将客户的写操作转换成事务,主节点数据写完后,再将给所有从节点发送广播,等待从节点的反馈,只要有超过一半的节点反馈ok,主节点就会给所有从节点的服务器发送提交消息,把主节点上的数据同步到从节点上

使用

配置zookeeper的home 在etc/profile配置文件下
修改zookeeper配置文件 在zookeeper的conf文件夹下 zoo开头的

Redis

redis能存储哪几种数据类型

字符串,list,set,hash,有序集合 BloomFilter,RedisSearch

什么是redis分布式锁

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放
把两个指令和成一条指令来使用

怎么找某个规律的key

使用keys指令扫描,如果这其中某个key被使用,那么就会发送阻塞,一直到使用完成才会继续

如果大量key设置同一时间过期

大量key同一时间过期,会发生卡顿,严重会雪崩,最好加上随机值

redis集群

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

redis淘汰策略

从设置了过期时间的数据集中挑选最少使用的数据淘汰
从设置了过期时间的数据集中挑选将要过期的数据淘汰
从设置了过期时间的数据集中挑选任意数据淘汰
从数据集中挑选最少使用的数据淘汰
从数据集中选择任意数据淘汰
禁止淘汰数据

redis是单线程还是多线程

Redis是单进程单线程的

redis过期键的删除策略

定时删除:
	设置过期时间的同时设置一个计时器,到时间了就自动删除
惰性删除:
	对过期键放入不管,到取出的时候检查该键是否过期
定期删除:
	定期检查全部key 并且删除过期的key 什么时候检查,检查多少,这个是由算法完成的

ActiveMQ

丢消息怎么办

使用持久化消息,或者开启事务

持久化消息很慢

开事务

消息的不均匀消费

每次只处理一条 处理完再去取消息

算法

快速排序

 public static void getSoat(int[] arr,int low,int high){
        //定义变量
        int i,j,t,temp;
        //定义退出条件
        if (low>high){
            return;
        }
        //变量赋值
        i = low;
        j = high;
        temp = arr[low];
        while (i<j){
            //先看右边
            while (temp<=arr[j]&&i<j){
                j--;
            }
            //再看左边
            while (temp>=arr[i]&&i<j){
                i++;
            }
            //交换位置
            if (i<j){
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
        }
        //交换基准
        arr[low] = arr[i];
        arr[i] = temp;
        //递归调用
        getSoat(arr, low,j-1 );
        getSoat(arr, j+1, high);
    }

冒泡排序

    public static int[] getList(int[] arr){
            //数组长度
            int len = arr.length;
            //临时变量
            int temp =0;
            //外层循环
            for (int i = 0; i < arr.length; i++) {
                //内层循环
                for (int j = 0; j < arr.length-1-i; j++) {
                    if (arr[j]>arr[j+1]){
                        //交换位置
                        temp = arr[j+1];
                        arr[j+1] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
            return arr;
        }

怎样防止表单重复提交

防止表单重复提交的方法有很多种,可以用前端js来做,表单提交结果返回之前按按钮无法点击
也可以通过生成token的方式,服务器验证token来防止,
不过最有效的还是在数据库层面,设置唯一约束,就可以防止出现重复数据

海量数据的优化

海量数据怎么解决
1.使用缓存来存储数据,这样能减少对数据库的操作,减轻服务器的压力
2.页面静态化
3.优化mysql的数据库
4.读写分离

接口安全怎么做

如何保证接口的安全?
	我们视接口的类型来决定是否对要保证这个接口的安全,像提供一些不敏感消息的接口,就可以不需要保障它的安全,只需要做一些常规的处理就好,比如反复调用接口和过载保护这些问题,
	过载保护说的是设置一个接口在一定时间内最多调用多少次,超过了这个次数的进行屏蔽
	但是如果是敏感信息的接口,比如用户个人信息这些,就必须要对接口进行处理,首先我们会对接口参数进行加密,和实效性验证,也就是每隔一段时间生成新的验证,并且使用https传输机制
	接口参数加密就是如果我要调用这个接口,必须携带一个独特参数,但是为了防止这个参数被人截取,伪造请求,就需要对这个独特参数进行加密,然后使用我独有的方法进行解密,比如MD5加盐这种,
	实效性验证就是我每隔一段时间就从新生成新的验证参数,并且通知那些可以调用我这个接口的模块,
	https就不用解释了,安全的网络传输协议

接口怎么防止恶意的调用

	分为前端和后端,前端防护的话,可以设置按钮在结果返回前不能点击,后端防护的话可以使用redis
	当第一个请求过来的时候,获取IP地址和url还有时间 并且把这些信息放入redis,当下一次请求过来,从redis中取出来,进行判断,Ip地址相同,访问的资源还是那个,并且时间小于我们设置的两次调用最小时间,就直接return掉 返回消息信息提示操作频繁

防止重复操作切面(watchadog待完善)

	。。。。

高并发问题

高并发解决方案
	做集群,使用负载均衡
	页面缓存,把应用生成的,不怎么会发生改变的页面缓存起来,这样就不用每次都生成新页面,节省cpu资源
	应用程序和静态资源分类,比如专供下载的资源单独开一台服务器,给这台服务器提供很高的带宽资源

二叉树的三种遍历方式

先序遍历

遍历顺序为 根 左节点 右节点

准备三个容器,先从根开始 然后再遍历左节点 再遍历右节点

中序遍历

遍历顺序为 左节点 根 右节点

准备三个容器,先从左节点 然后再根 再遍历右节点

后续遍历

遍历顺序为 左节点 右节点 根

准备三个容器,先从左节点 然后再右节点 再遍历根

思想

获取初始根节点 然后获取子节点 判断子节点有没有子节点,如果没有,说明该节点为根节点,如果有子节点 再递归重复这个过程

类加载器和原理

什么是类加载器

负责把java类加载到java虚拟机中的java类,类加载器基于三个机制,委托,可见性,和单一性
委托:
	委托机制是指将加载一个类的请求交给父类加载器,一层层递交上去,如果最顶层的无法加载,再由次一级尝试加载,次一级无法加载,再给次一级
可见性:
	子类加载器可以看见父类的加载的类,而父类看不到子类加载的类
单一:
	一个类只会被加载一次

加载器

顶级加载器
	Bootstrap,初始类加载器.负责加载JDK系统类
二级加载器
	Extension,次一级的加载器,负责加载jre扩展类
三级加载器
	Application,加载应用类

StringBuffer和StringBuilder的区别

StringBuffer和StringBuilder的区别
	说到这两个,就要提起String,因为String是不可变的,所有对String的操作,都会创建一个新的对象,所有如果要对字符串进行操作.我们一般会采用StringBuffer和StringBuilder,这两种的区别,就是StringBuffer是线程安全的,它其中的各种操作字符串的方法都是加上了synchronized关键字的,所以线程是安全的,在一个线程执行完这个对应的方法之前,其他线程无法对该StringBuffer对象进行操作,而StringBuilder是线程不安全的,一般如果我们在单线程下,会采用StringBuilder,而多线程情况下,会使用StringBuffer,与他们两关系类似的有HashTable和HashMap,vector,ArrayList
	他们之间的效率对比的话 StringBuilder>StringBuffer>String

MVC和三层架构

三层架构是最基本的项目分层结果,而MVC则是三层架构的一个变体,MVC是一种好的开发模式。首先你要明白MVC分别代表的是什么意思.
M 即Model(模型层),主要负责处理业务逻辑以及数据库的交互
V 即View(视图层),主要用于显示数据和提交数据
C 即Controller(控制器),主要是用作捕获请求并控制请求转发

三层:UI 界面层 BLL 业务逻辑层,DAL数据访问层,Model 实体层
MVC中的的M 不是三层中的Model(实体层),他其实包括三层中的 BLL,DAL,Model,这是非常要注意的,这也是他们之间的区别的关键所在

其有点有如下:
低耦合性
高重用性和可适用性
较低的生命周期成本
快速的部署
可维护性
有利于软件工程化管理

当然优点也有缺点,那就是内部结构复杂,不容易理解,文件数量大,管理难度自然也就大

MVC设计模式…
三层架构…
他们细分之后得到的是:View(UI)、BIZ(BLL)、DAO(DAL)、Entity(Model)、Controller
MVC把 BIZ(BLL)、DAO(DAL)、Model(Entity) 统一称之为 模型(MODEL),得到:View、Controller、模型(MODEL)
三层 在我使用中 暂未体会到控制器的存在,完全是:UI、DAO、BLL

他们相同的设计理念就是:把视图设计与数据持久化进行分离,从而降低耦合性,易于扩展,提高团队开发效率。

三层是基于业务逻辑来分的,而mvc是基于页面来分的
根本就没有什么可比性。
其实两个一起用我感觉很好

MVC模式是一种复合设计模式,一种解决方案
三层是种软件架构,通过接口实现编程
三层模式是体系结构模式,MVC是设计模式
三层模式又可归于部署模式,MVC可归于表示模式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值