日常的小知识
-----响应码含义----
1xx:指示信息,表示请求已经接收
2xx:成功,表示请求已经被服务器成功接收,理解
3xx:重定向
4xx:客户端错误-请求有语法错误或者请求是无法实现
5xx:服务器端的错误,服务器未能实现合法的请求
200 OK 表示请求成功
400 Bad Request 请求语法
401 未经授权
403 请求接收到,拒绝提供服务
404 请求资源不存在 潜台词:你输错url了,第二检查你的服务器是否存在此资源
500 服务器发生了不可预期的错误 java后台Exception报错了。
503 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
500:
程序运行过程中报500错误肯定是程序的代码逻辑,或者配置文件逻辑不对,这时应该看控制台显示的错误信息去定位错误并解决。(控制台那么多的报错信息怎么看?找自己看得懂的、熟悉的、有印象的,然后再找caused by:显示的错误信息caused by 往往就是我们产生错误的根本原因。)
404:
报404错误通常就是路径错误或者是缓存产生的。路径错误可能是因为浏览器里的路径不正确,或者路径中的单词错误等。缓存的话多清理几次Tomcat和Project,最好把Tomcat里的缓存文件给删除。
一启动Tomcat(一运行程序)就报错(弹出一个提示框),出现这种情况,多半是因为web.xml配置文件的配置有误。根据控制台的报错信息,查看caused by 那行显示的错误信息定位错误,并解决。
3198-128748-38933
除零报异常java.lang.ArithmeticException: / by zero
------线程池七个参数------
在自定义线程池或者使用Executors创建线程池时,都会使用到ThreadPoolExecutor这个类的构造方法进行线程池的创建,而这个类的构造方法中就包含著名的七大参数
(1)corePoolSize:
核心池的大小。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;(相当于常开的服务窗口满了,顾客到候客区等待)
(2)maximumPoolSize:
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(即阻塞队列,相当于前面所讲的候客区)中,如果工作队列满了,才会创建一个新线程(候客区也满了,就会增加新的窗口),然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
(3)keepAliveTime:
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
(4)unit(类型为:TimeUnit):
参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
(5)workQueue:
一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
linkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用linkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②linkedBlockingQueue
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
(6)threadFactory:
线程工厂,主要用来创建线程;
(7)RejectedExecutionHandler handler:
拒绝策略,当线程池的线程数已经达到线程池能够创建的最大线程数且阻塞队列也已经满了的时候,还有线程想要被创建执行,就会执行拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
-------1.8steam流-------
List
if(CollectionUtils.isEmpty(orderbases)) {
log.info("当前支付单号: {} 查询不到订单信息, 无法取消!", payOrderId);
return false;
}
List
List
Map
String orderNoListStr = orderDistributionQueryRequest.getOrderId();
if(StringUtils.isNotEmpty(orderNoListStr)){
String[] spilt = orderNoListStr.split("n");
//结果集-初始为数据长度
List
Arrays.stream(spilt).forEach(orderNO ->{
//去空格
String trim = orderNO.trim();
if(StringUtils.isNotEmpty(trim)){
//放入结果集
orderNoList.add(trim);
}
});
if (!StringUtils.isEmpty(orderSearchRequest.getOrderId())) {
String separator = orderSearchRequest.getOrderId().contains(Constant.SEPARATOR_BREAK_LINE) ? Constant.SEPARATOR_BREAK_LINE : Constant.SEPARATOR_ENTER;
List
.filter(item -> !StringUtils.isBlank(item)).collect(Collectors.toList());
if (orderIds.size() > 20) {
throw new BizException("orderIdTooLong");
}
orderSearchRequest.setOrderIds(orderIds);
}
-----set去重----
int[] arr = {1,2,2,3,3,3};
Set set = new HashSet
for(int num:arr){
set.add(num);
}
HashSet
List
for(String str : list) {
if(set.add(str)) {
result.add(str);
}
}
list.clear;
list.addAll(result);
}
// 遍历后判断赋给另一个List集合,保持原来顺序
public static void ridRepeat1(List
System.out.println("list = [" + list + "]");
List
for (String str : list) {
if (!listNew.contains(str)) {
listNew.add(str);
}
}
System.out.println("listNew = [" + listNew + "]");
}
// Set集合去重,保持原来顺序
public static void ridRepeat2(List
System.out.println("list = [" + list + "]");
List
Set set = new HashSet();
for (String str : list) {
if (set.add(str)) {
listNew.add(str);
}
}
System.out.println("listNew = [" + listNew + "]");
}
//利用java8的stream去重
List uniqueList = list.stream().distinct().collect(Collectors.toList());
System.out.println(uniqueList.toString());
-----同步锁对象-----
private ReentrantLock lock = new ReentrantLock();
public void transfer(int from,int to,int money){
//同步锁上锁
lock.lock();
try{
//转出金额
accounts[from] -= money;
System.out.printf("从%d转出%d到%d%n",from,money,to);
//转入金额
accounts[to] += money;
//输出总金额
System.out.println("银行的总金额是:"+getTotalAccount());
}finally{
//释放锁
lock.unlock();
}
}
------比较----
Collections.sort(currencys, (CurrencyDO o1, CurrencyDO o2) -> {
if (Integer.valueOf(o1.getOrderBy()) > Integer.valueOf(o2.getOrderBy())) {
return 1;
} else if (Integer.valueOf(o1.getOrderBy()) < Integer.valueOf(o2.getOrderBy())) {
return -1;
}
return 0;
});
-----日期转换----
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("把当前时间转换成字符串:" + sdf.format(new Date()));
Date date = null;
try {
// 注意格式需要与上面一致,不然会出现异常
date = sdf.parse("2005-12-15 15:30:23");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("字符串转换成时间:" + date);
select * from table limit 2 offset 1;
含义是从第1条(不包括)数据开始取出2条数据,limit后面跟的是2条数据,offset后面是从第1条开始读取,即读取第2,3条
-----数据库死锁问题-----
//数据库死锁
select sess.sid,
sess.serial#,
lo.oracle_username,
lo.os_user_name,
ao.object_name,
lo.locked_mode
from v$locked_object lo,
dba_objects ao,
v$session sess
where ao.object_id = lo.object_id and lo.session_id = sess.sid;
alter system kill session '94,33911'; --sid,serial#
另一种
select 'alter system kill session '''||sess.sid||','||sess.serial#||''';'
from v$locked_object lo,
dba_objects ao,
v$session sess
where ao.object_id = lo.object_id and lo.session_id = sess.sid;
-----sql执行顺序---
select[distinct]
from
join(如left join)
on
where
group by
having
union
order by
limit
select distinct sum(a.OrderPrice) As order1,sum(d.OrderPrice) As order2
from orders a
left join (select c.* from Orders c) d
on a.O_Id = d.O_Id
where a.Customer='Bush' or a.Customer = 'Adams' or a.Customer = 'Carter'
group by a.Customer
having sum(a.OrderPrice) > 1500
union
select distinct sum(a.OrderPrice) As order1,sum(e.OrderPrice) As order2
from orders a
left join (select c.* from Orders c) e
on a.O_Id = e.O_Id
where a.Customer='Bush' or a.Customer = 'Adams' or a.Customer = 'Carter'
group by a.Customer
having sum(a.OrderPrice) > 2000
order by order1
limit 1
-----是长度还是下表-----
substr(startIndex,lenth): 第二个参数是截取字符串的长度(从起始点截取某个长度的字符串);
如果没有指定 length,那么返回的字符串包含从 startIndex到结尾的字符。
substring(startIndex, endIndex): 第二个参数是截取字符串最终的下标
查询8条数据,索引从5到12
select * from t_user limit 5,8;
-----String方法-----
1 length()字符串的长度
2 charAt()截取一个字符
3 getchars()截取多个字符并由其他字符串接收
String a = "Hello Word";
char[] b = new char[10];
a.getChars(0, 5, b, 0);
System.out.println(b);
4 getBytes()将字符串变成一个byte数组
5 toCharArray()将字符串变成一个字符数组
6 equals()和equalsIgnoreCase()比较两个字符串是否相等,前者区分大小写,后者不区分
7 startsWith()和endsWith()判断字符串是不是以特定的字符开头或结束
8 toUpperCase()和toLowerCase()将字符串转换为大写或小写
9 concat() 连接两个字符串
10 trim()去掉起始和结束的空格
11 substring()截取字符串
12 indexOf()和lastIndexOf()前者是查找字符或字符串第一次出现的地方,后者是查找字符或字符串最后一次出现的地方
13 compareTo()和compareToIgnoreCase()按字典顺序比较两个字符串的大小,前者区分大小写,后者不区分
14 replace() 替换
-----------------Java中的object九大方法----------
1.clone方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
2.getClass方法
final方法,获得运行时类型。
3.toString方法
该方法用得比较多,一般子类都有覆盖。
4.finalize方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
5.equals方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
6.hashCode方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
7.wait方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
8.notify方法
该方法唤醒在该对象上等待的某个线程。
9.notifyAll方法
该方法唤醒在该对象上等待的所有线程。
-------mybatis-plus-------
Employee employee = new Employee();
employee.setLastName("东方不败");
employee.setEmail("dfbb@163.com");
employee.setGender(1);
employee.setAge(20);
emplopyeeDao.insert(employee);
Employee employee = new Employee();
employee.setId(1);
employee.setLastName("更新测试");
//emplopyeeDao.updateById(employee);//根据id进行更新,没有传值的属性就不会更新
emplopyeeDao.updateAllColumnById(employee);//根据id进行更新,没传值的属性就更新为null
Employee employee = emplopyeeDao.selectById(1);
Employee employeeCondition = new Employee();
employeeCondition.setId(1);
employeeCondition.setLastName("更新测试");
//若是数据库中符合传入的条件的记录有多条,那就不能用这个方法,会报错
Employee employee = emplopyeeDao.selectOne(employeeCondition); //这个方法的sql语句就是where id = 1 and last_name = 更新测试
Map
columnMap.put("last_name","东方不败");//写表中的列名
columnMap.put("gender","1");
List
List
idList.add(1);
idList.add(2);
idList.add(3);
List
List
emplopyeeDao.deleteById(1);
Map
columnMap.put("gender",0);
columnMap.put("age",18);
emplopyeeDao.deleteByMap(columnMap);
List
idList.add(1);
idList.add(2);
emplopyeeDao.deleteBatchIds(idList);
分页查询年龄在18 - 50且gender为0、姓名为tom的用户:
List
new EntityWrapper
.between("age",18,50)
.eq("gender",0)
.eq("last_name","tom")
);
查询gender为0且名字中带有老师、或者邮箱中带有a的用户:
List
new EntityWrapper
.eq("gender",0)
.like("last_name","老师")
//.or()//和or new 区别不大
.orNew()
.like("email","a")
);
查询gender为0,根据age排序,简单分页:
List
new EntityWrapper
.eq("gender",0)
.orderBy("age")//直接orderby 是升序,asc
.last("desc limit 1,3")//在sql语句后面追加last里面的内容(改为降序,同时分页)
);
分页查询年龄在18 - 50且gender为0、姓名为tom的用户:
List
new Page
Condition.create()
.between("age",18,50)
.eq("gender","0")
);
根据条件更新:该案例表示把last_name为tom,age为25的所有用户的信息更新为employee中设置的信息。
@Test
public void testEntityWrapperUpdate(){
Employee employee = new Employee();
employee.setLastName("苍老师");
employee.setEmail("cjk@sina.com");
employee.setGender(0);
emplopyeeDao.update(employee,
new EntityWrapper
.eq("last_name","tom")
.eq("age",25)
);
}
根据条件删除:该案例表示把last_name为tom、age为16的所有用户删除。
emplopyeeDao.delete(
new EntityWrapper
.eq("last_name","tom")
.eq("age",16)
);
-----线程----
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
// 从现在开始1秒钟之后,每隔1秒钟执行一次job1
service.scheduleAtFixedRate(
new ScheduledExecutorTest("job1"), initialDelay1,
period1, TimeUnit.SECONDS);
第一个command参数是任务实例,
第二个initialDelay参数是初始化延迟时间,
第三个period参数是间隔时间,
第四个unit参数是时间单元。
ScheduledFuture> result = executor.scheduleAtFixedRate(task,2, 5, TimeUnit.SECONDS);
在延迟2秒之后开始执行首个任务,之后每隔5秒执行一个任务,也就是固定间隔时间执行一次任务,而不是等到上个任务执行结束。
ScheduledFuture> result = executor.scheduleWithFixedDelay(task,2, 5, TimeUnit.SECONDS);
在延迟2秒后开始执行首个任务,总是等到每个任务执行完毕后再等待(间隔)5秒开始执行下个任务。
----数据库---
on duplicate key update 当数据存在执行更新操作,不存在执行插入操作(mysql 数据库的)
update_time = now() (mysql 数据库的)
select * from table limit 2 offset 1;
含义是从第1条(不包括)数据开始取出2条数据,limit后面跟的是2条数据,offset后面是从第1条开始读取,即读取第2,3条
--T1(2,3)
--T2(1,2,3,4)
--ALL
-- >ALL 父查询中的结果集大于子查询中每一个结果集中的值,则为真
SELECt * FROM T2 WHERe N>ALL (SELECt N FROM T1)
-- >ANY,SOME 父查询中的结果集大于子查询中任意一个结果集中的值,则为真
SELECt * FROM T2 WHERe N>ANY(SELECt N FROM T1)
SELECt * FROM T2 WHERe N>SOME(SELECt N FROM T1)
-- =ANY 与子查询IN相同
SELECt * FROM T2 WHERe N=ANY (SELECt N FROM T1)
SELECt * FROM T2 WHERe N IN (SELECt N FROM T1)
-- <>ANY 与NOT IN
--<>ANY
--OR作用 父查询中的结果集不等于子查询中的a或者b或者c,则为真
SELECt * FROM T2 WHERe N <>ANY(SELECt * FROM T1)
--NOT IN
--AND作用 父查询中的结果集不等于子查询中任意一个结果集中的值,则为真
SELECt * FROM T2 WHERe N NOT IN(SELECt * FROM T1)
--建表----
create table EHREOS.T_NHR_BELONGSYSTEM
(
id VARCHAr2(16) not null primary key,
system VARCHAr2(50) not null,
pdcode VARCHAr2(10) not null
)
tablespace NNC_DATA01;
comment on table EHREOS.T_NHR_BELONGSYSTEM
is '所属系统表';
comment on column EHREOS.T_NHR_BELONGSYSTEM.id
is '主键';
comment on column EHREOS.T_NHR_BELONGSYSTEM.system
is '系统';
comment on column EHREOS.T_NHR_BELONGSYSTEM.pdcode
is 'PD码';
--索引---
alter table EHREOS.T_NHR_BELONGSYSTEM
add constraint PK_T_NHR_BELonGSYSTEM primary key (ID)
using index
tablespace NNC_INDEX01;
--新加字段
alter table dbwlhr.T_NHR_PBC_PROCESS_NAME add(SAPSTATUS varchar2(10));
comment on column dbwlhr.T_NHR_PBC_PROCESS_NAME.SAPSTATUS is 'SAP状态0:没传过 1:失败 2:成功';
--修改字段
ALTER TABLE 表名称 MODIFY(列名 1 类型 [DEFAULT 默认值],列名 1 类型 [DEFAULT 默认值)
alter table dbwlhr.T_NHR_PBC_PROCESS_NAME modify(SAPSTATUS varchar2(10));
--修改字段名
ALTER TABLE 表名称 RENAME COLUMN 原列名 TO 新列名
alter table dbwlhr.T_NHR_PBC_PROCESS_NAME RENAME COLUMN STATUS TO SAPSTATUS
--Oracle修改字段类型和
ALTER TABLE tableName modify(columnName 类型);
--修改长度语句:
alter table test modify(name varchar(255));
alter table EHREOS.T_NHR_RELIEVE modify(MODIFY_USER varchar(55));
----实现Runnable接口比继承Thread类所具有的优势:---
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
-----三次握手,-------
第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。
发送完毕后,服务器端进入 SYN_RCVD 状态。
第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
-----------四次挥手----------
第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
----------
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSonString(list);
System.out.println("JSON.toJSonString(list)==>"+str1);
String str2 = JSON.toJSonString(user1);
System.out.println("JSON.toJSonString(user1)==>"+str2);
System.out.println("n****** JSON字符串 转 Java对象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
System.out.println("n****** Java对象 转 JSON对象 ******");
JSonObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
System.out.println("n****** JSON对象 转 Java对象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
//转换为list
List
System.out.println("list为:"+list2+",其类型为:"+list2.getClass());
1、
首先将json字符串转换为json对象,然后再解析json对象,过程如下。
JSonObject jsonObject = JSONObject.fromObject(jsonStr);
根据json中的键得到它的值
String name = jsonObject.getString("name");
int num = jsonObject.getInt("num");
String sex = jsonObject.getString("sex");
int age = jsonObject.getInt("age");
2、同样先将json字符串转换为json对象,再将json对象转换为java对象,如下所示。
JSonObject obj = new JSonObject().fromObject(jsonStr);//将json字符串转换为json对象
将json对象转换为java对象
Person jb = (Person)JSONObject.toBean(obj,Person.class);//将建json对象转换为Person对象
LIst
3、先将java对象转换为json对象,在将json对象转换为json字符串
JSonObject json = JSONObject.fromObject(obj);//将java对象转换为json对象
String str = json.toString();//将json对象转换为字符串
------servlet生命周期--------
1 描述了 Servlet 的生命周期。按照功能的不同,大致可以将 Servlet 的生命周期分为三个阶段,分别是初始化阶段、运行阶段和销毁阶段。
1)初始化阶段
当客户端向 Servlet 容器发出 HTTP 请求要求访问 Servlet 时,Servlet 容器首先会解析请求,检查内存中是否已经有了该 Servlet 对象,如果有,则直接使用该 Servlet 对象,如果没有,则创建 Servlet 实例对象,然后通过调用 init() 方法实现 Servlet 的初始化工作。需要注意的是,在 Servlet 的整个生命周期内,它的 init() 方法只能被调用一次。
2)运行阶段
这是 Servlet 生命周期中最重要的阶段,在这个阶段中,Servlet 容器会为这个请求创建代表 HTTP 请求的 ServletRequest 对象和代表 HTTP 响应的 ServletResponse 对象,然后将它们作为参数传递给 Servlet 的 service() 方法。
service() 方法从 ServletRequest 对象中获得客户请求信息并处理该请求,通过 ServletResponse 对象生成响应结果。
在 Servlet 的整个生命周期内,对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,并且创建新的 ServletRequest 和 ServletResponse 对象,也就是说,service() 方法在 Servlet 的整个生命周期中会被调用多次。
3)销毁阶段
当服务器关闭或 Web 应用被移除出容器时,Servlet 随着 Web 应用的关闭而销毁。在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法,以便让 Servlet 对象释放它所占用的资源。在 Servlet 的整个生命周期中,destroy() 方法也只能被调用一次。
需要注意的是,Servlet 对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭或 Web 应用被移除出容器时,Servlet 对象才会销毁。
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0
Integer i = 100;
Integer j = 100;
System.out.println(i == j); //true
Integer i1 = 128;
Integer j1 = 128;
System.out.println(i1 == j1); //false
Java中的Integer对于-128到127之间的数,会进行缓存。
所以 Integer i = 100 时,会将100进行缓存,下次再写Integer j = 100时,就会直接从缓存中取,而不会new了。
-----枚举---
public enum DismissType {
LEAVEOFF("辞", "04"), FROMOFF("离", "10"), DISMISS("退",
"11"), DIE("亡", "09");
private String name;
private String value;
DismissType(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
}
------深浅拷贝-----
浅拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
深拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。
深拷贝相比于浅拷贝速度较慢并且花销较大。
浅拷贝实现 Cloneable,深拷贝是通过实现 Serializable 读取二进制流
并发,就像一个人(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。
并行,就是2个人喂2个孩子,两个孩子也同时在吃饭。
客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,
并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出的请求,请求信息及其他信息,
当servlet处理完业务逻辑后会将信息放入到response并响应到客户端。
SQL INNER JOIN(简单的 JOIN)、SQL LEFT JOIN、SQL RIGHT JOIN、SQL FULL OUTER JOIN FULL JOIN 称为 FULL OUTER JOIN。
------判空-----
CollectionUtils.isEmpty(list类型)
StringUtils.isBlank(String类型)
BeanUtils.copyProperties(有值的,没值的)
Sychronized和lock的区别
1.Sychronized 内置的java关键字,Lock锁是一个java类
2.Sychronized 无法判断获取锁的状态,Lock锁可以判断是否获取到了锁.
3.Sychronized 会自动释放锁lock必须手动释放锁,如果不释放锁,死锁
4.Sychronized 线程一(获得锁,阻塞),线程二(等待,傻傻的等),Lock锁就不一定会等待下去.
5.Sychronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以中断锁,非公平(可以自己设置)
6.Sychronized 适合锁少量的代码的同步问题,Lock适合锁大量的代码同步问题.
多线程之如何查看线程死锁 JconsoleJconsole是JDK自带的图形化界面工具。
1、使用JDK给我们的的工具JConsole,可以通过打开cmd然后输入jconsole打开。
连接到需要查看的进程。 打开线程选项卡,然后点击左下角的“检测死锁”
2、Jstack是JDK自带的命令行工具,主要用于线程Dump分析。
我们先用Jps来查看java进程id。 查看进程,并输入jstack (进程号)
如果是死锁的话会提示,然后找到对应的代码就好了
mysql执行计划: explain sql语句
讲讲threadlocal:
1.ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据
2.ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
3.如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把设置的key,value,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决办法是,在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清楚Entry对象
4.ThreadLocal经典的应用场景就是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)
先说HashMap的Put方法的大体流程:
1.根据Key通过哈希算法与与运算得出数组下标
2.如果数组下标位置元素为空,则将key和value封装为Entry对象(JDK1.7中是Entry对象,JDK1.8中是Node对象)并放入该位置
3.如果数组下标位置元素不为空,则要分情况讨论
a.如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进行扩容,如果不用扩容就生成Entry对象,使用头插法添加到当前位置的链表中
你们项目如何排查JVM问题
b.如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红黑树Node,还是链表Node
1.如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存 在则更新value
ii.如果此位置上的Node对象是链表节点,则将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插入到链表中,插入到链表后,会看当前链表的节点个数,如果超过了8,那么则会将该链表转成红黑树
iii.将key和value封装位Node插入到链表或红黑树中后,再判断是否需要进行扩容,如果需要就扩容,如果不需要就介绍PUT方法
notify()不释放锁,wait()释放锁
线程之间如何通信:线程间通信的几种实现方式_InterfaceJ的博客-CSDN博客_线程通信
1.使用 volatile 关键字
2.使用Object类的wait() 和 notify() 方法
3.使用JUC工具类 CountDownLatch
4.使用 ReentrantLock 结合 Condition
5.基本LockSupport实现线程间的阻塞和唤醒
什么时候@Transactional失效?
一,特性:1,一般在service里加@Transactional注解,不建议在接口上添加,加了此注解后此类会纳入spring事务管理中,每个业务方法执行时,都会开启一个事务,不过都是按照相同的管理机制。
2,@Transactional注解只能应用到public修饰符上,其它修饰符不起作用,但不报错。
3,默认情况下此注解会对unchecked异常进行回滚,对checked异常不回滚。
那什么是unchecked,什么是checked呢?
通俗的说,编译器能检测到的是checked,检测不到的就是unchecked。
派生于Error或者RuntimeException(比如空指针,1/0)的异常称为unchecked异常。
继承自Exception得异常统称为checked异常,如IOException、TimeoutException等。
4、只读事务:
@Transactional(propagation=Propagation.NOT_SUPPORTED,readonly=true)
只读标志只在事务启动时应用,否则即使配置也会被忽略。
启动事务会增加线程开销,数据库因共享读取而锁定(具体跟数据库类型和事务隔离级别有关)。通常情况下,仅是读取数据时,不必设置只读事务而增加额外的系统开销。
二:事务传播模式Propagation枚举了多种事务传播模式,部分列举如下:
1、REQUIRED(默认模式):业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。
2、NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。
3、REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。
4、 MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。
5、SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。
6、NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。
7、NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。
三,解决注解不回滚问题1,检查方法是不是public
2,检查异常是不是unchecked异常
3,如果是checked异常也想回滚的话,注解上写明异常类型即可
@Transactional(rollbackFor=Exception.class)
union关键字默认去重,如果使用union all 可以包含重复项
USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo(
id INT PRIMARY KEY,#主键
stuName VARCHAr(20) NOT NULL UNIQUE,#非空
gender CHAr(1) CHECK(gender='男' OR gender ='女'),#检查
seat INT UNIQUE,#唯一
age INT DEFAULT 18,#默认约束
majorId INT REFERENCES major(id)#外键
);
CREATE TABLE major(
id INT PRIMARY KEY,
majorName VARCHAr(20)
);
@Deprecated注解功能若某类或某方法加上该注解之后,表示此方法或类不再建议使用,调用时也会出现删除线,但并不代表不能用,只是说,不推荐使用,因为还有更好的方法可以调用。
如何确定是垃圾:
1.引用计数法
2.可达性分析
垃圾回收:
1:标记-清除法:
2:复制算法:
3:标记-整理算法:
4:Generational Collection(分代收集)算法 新生代 ,老年代 ,永久代
stw(stop the world)
垃圾收集器:
Serial 收集器:一个采用单个线程并基于复制算法工作在新生代的收集器,进行垃圾收集时,必须暂停其它所有的工作线程(Stop The World),是 Client 模式下 JVM 的默认选项,-XX:+UseSerialGC
Serial Old 收集器:一个采用单线程基于标记-整理算法并工作在老年代的收集器
ParNew 收集器:Serial 收集器的多线程版本(使用多个线程进行垃圾收集),-XX:+UseParNewGC
CMS 收集器(Concurrent Mark Sweep):一种以尽量减少停顿时间为目标的收集器,工作在老年代,基于标记-清除算法实现,-XX:+UseConcMarkSweepGC
Parallel Scavenge 收集器:一个采用多线程基于复制算法并工作在新生代的收集器,也被称作是吞吐量优先的 GC,是早期 jdk1.8 等版本中 Server 模式 JVM 的默认 GC 选择,-XX:+UseParallelGC,使用 Parallel Scavenge(年轻代)+ Serial Old(老年代)的组合进行 GC
Parallel Old 收集器:一个采用多线程基于标记-整理算法并工作在老年代的收集器,适用于注重于吞吐量及 CPU 资源敏感的场合,-XX:+UseParallelOldGC,使用 Parallel Scavenge(年轻代)+ Parallel Old(老年代)的组合进行 GC
G1 收集器:jdk1.7 提供的一个工作在新生代和老年代的收集器,基于标记-整理算法实现,在收集结束后可以避免内存碎片问题,一种兼顾吞吐量和停顿时间的 GC,是 Oracle jdk1.9 以后的默认 GC 选项
jdk1.7 默认垃圾收集器 Parallel Scavenge(新生代)+ Serial Old(老年代)
jdk1.8 默认垃圾收集器 Parallel Scavenge(新生代)+ Serial Old(老年代)
jdk1.9 默认垃圾收集器 G1
JVM 调优工具介绍 jdk自带的工具 jconsole VisualVM6.1 jmap 命令
查看堆内存分配和使用情况
./jmap -heap 31 //31 为程序的进程号
6.2 Top 命令监控结果
通过使用 top 命令进行持续监控发现此时 CPU 空闲比例为 85.7%,剩余物理内存为 3619M,虚拟内存 8G 未使用。持续的监控结果显示进程 29003 占用系统内存不断在增加,已经快得到最大值。
6.3 Jstat 命令监控结果
./jstat –cutil 29003 1000 100 使用 jstat 命令对 PID 为 29003 的进程进行 gc 回收情况检查,发现由于 Old 段的内存使用量已经超过了设定的 80%的警戒线,导致系统每隔一两秒就进行一次 FGC,FGC 的次数明显多余 YGC 的次数,但是每次 FGC 后 old的内存占用比例却没有明显变化—系统尝试进行 FGC 也不能有效地回收这部分对象所占内存。同时也说明年轻代的参数配置可能有问题,导致大部分对象都不得不放到老年代来进行 FGC 操作
日志:
### set log levels ###
log4j.rootLogger = DEBUG,console,file
### 输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n
### 输出到日志文件 ###
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/hou.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
# 日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
–Xms 设置堆的最小空间大小,默认为物理内存的 1/64
–Xmx 设置堆的最大空间大小,默认为物理内存的 1/4
–Xss 设置每个线程的堆栈大小,jdk1.5 以后默认为 1M
–Xdebug 在调试模式下运行
–XX:NewSize 设置新生代最小空间大小
–XX:MaxNewSize 设置新生代最大空间大小
–XX:PermSize 设置永久代最小空间大小
–XX:MaxPermSize 设置永久代最大空间大小
–XX:NewRatio 设置老年代与新生代的比值,默认值为 3
–XX:SurvivorRatio 设置年轻代中 Eden 区与 2 个 Survivor 区的比值
–XX:MaxTenuringThreshold 表示一个对象如果在 Survivor 区移动的次数达到设置值还没有被垃圾回收就进入老年代(如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入老年代)
–XX:PretenureSizeThreshold 直接晋升到老年代的对象大小
–XX:+PrintGCDetails 输出详细的 GC 处理日志
–XX:+HeapDumponOutOfMemoryError 让 JVM 碰到 OOM 场景时输出 dump 信息
传map 直接取
栈和队列的主要区别 线程池推荐用哪个队列
itar for循环 iter 高效for循环
静态代理模式// 抽象角色(租房)
public interface Rent {
public void rent();
}
房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
中介
public class Proxy implements Rent{
// 被代理的房东
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public Host getHost() {
return host;
}
public void setHost(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent(); // 调用房东的方法
}
}
客户
public class Client {
public static void main(String[] args) {
// 被代理的房东对象
Host host = new Host();
// 代理角色(中介)
Proxy proxy = new Proxy(host);
// 客户租房
proxy.rent();
}
}
动态代理实例
public interface UserService {
void add();
void delete();
void update();
void query();
}
//真实对象 想要在方法加入日志
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("执行添加用户方法");
}
public void delete() {
System.out.println("执行删除用户方法");
}
public void update() {
System.out.println("执行更新用户方法");
}
public void query() {
System.out.println("执行查询方法");
}
}
public class ProxyInvocationHandler implements InvocationHandler {
// 真实对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成代理类对象
public Object getTarget(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 加入日志
show(method.getName());
method.invoke(target,args);
return null;
}
public void show(String name){
System.out.println("执行"+name+"方法");
}
}
public class Client {
public static void main(String[] args) {
// 真实对象
UserService userService = new UserServiceImpl();
// 生成代理类
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(userService);
UserService target = (UserService) proxyInvocationHandler.getTarget();
// 执行方法
target.delete();
}
}
很多数查询最大十个
countDownLatch
重新eques不重写hashcode会怎样?
常见的几个默认值和扩容倍数:
StringBuffer的默认容量是16 扩容1.5倍
arrayList的默认容量是10 当到达极限的时候也是会自动扩容两倍
Hashmap的默认容量是16 但是当饱和度达到整个容量的0.75就会进行自动扩容,而扩容的倍数为原先的两倍
Hashtable(和上面的StiringBuffer都有synchronized关键字修饰,是线程安全的),它的默认大小是11 它和Hashmap一样都是在达到自身容量0.75的时候就扩容。扩容是原来的两倍+1。
缓存穿透、缓存击穿、缓存雪崩区别和解决方案一、缓存处理流程
前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
二、缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
三、缓存击穿
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
设置热点数据永远不过期。
加互斥锁,互斥锁参考代码如下:
说明:
1)缓存中有数据,直接走上述代码13行后就返回结果了
2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
四、缓存雪崩
描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期。
dubbo,zookeeper,eureka之间的关系与区别 CAP原则先来解释下分布式系统中的CAP原则:指的是在一个分布式系统中,C - Consistency(一致性)、 A - Availability(可用性)、P - Partition tolerance(分区容错性),三者不可兼得。
其中,P - Partition tolerance(分区容错性)原则是必不可少的。
dubbo,zookeeper,eureka的关系:1、Dubbo相当与Spring Cloud
Dubbo是个微服务整体架构的框架,提供的功能包括服务注册发现,远程调用,监控等等。对标的项目是spring cloud。但Spring Cloud是一个系列的软件,有很多组件来拼装提供微服务的总体架构。Dubbo自己全封装了。
2、Zookeeper集成在Dubbo中以后,相当于Spring Cloud中的Eureka
Dubbo的服务发现模块基于zookeeper实现;
Eureka是Spring Cloud之下一个专门负责微服务服务注册和发现的组件,Eureka就是为了服务发现而设计的。是Dubbo对应的概念的一个部分。
3、原本的Zookeeper
在概念上,Zookeeper是用来保证分布式一致性的一个软件,不是为了服务发现注册而设计的。只不过它的特性也可以被二次开发成服务发现注册中心罢了,所以在Duboo这里被改造成做注册用了。
4、Zookeeper保证C P
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
5、Eureka保证A P
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪
myaql四种隔离级别
事务的 四个特征(ACID)
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。
1 、原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
2 、一致性。事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
3 、隔离性。一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
4 、持续性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
Mysql的四种隔离级别
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
出现问题
这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
双亲委派模型


