synchronized的用法
synchronized是Java中的一个关键字,是一种同步锁,修饰的对象有以下几种:
1、修饰一个代码块,被修饰的代码块称为同步语句块,其作用范围是括号括起来的代码,作用的对象是调用这个代码块的对象。
2、修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。
3、修饰一个静态的方法,其作用范围是整个静态方法,作用的对象是这个类的所有对象。
4、修饰一个类,其作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
Redis哈希槽
https://blog.youkuaiyun.com/tianyeshiye/article/details/79600014
数据结构
https://blog.youkuaiyun.com/yeyazhishang/article/details/82353846
1、数组Array
2、栈 stack
3、队列 queue
4、链表 linked list
5、树 tree
6、散列表 hash
7、堆 heap
8、图 graph
三次握手四次挥手
https://blog.youkuaiyun.com/qq_38950316/article/details/81087809
jsp与servlet区别
https://blog.youkuaiyun.com/forward__/article/details/57079115
重定向和转发的区别,以及能到百度吗
https://blog.youkuaiyun.com/liubin5620/article/details/79922692
区别:
转发(forward)浏览器的地址栏不变,重定(Redirect)向浏览器的地址栏改变。
转发是服务器行为,重定向是客户端行为。
转发是浏览器只做一次访问请求。重定向浏览器至少做两次访问请求。
转发两次跳转之间传输的信息不会丢失,重定向两次跳转之间传递的信息会丢失。
转发和重定向的选择:
1、重定向的速度比转发慢,因为浏览器还要发出一个新的请求,如果在使用转发和重定向都无所谓的情况下,建议使用转发。
2、因为转发只能访问当前web的应用程序,所以不同web应用程序之间的访问,特别是要访问到另一个web站点上资源的情况,就只能使用重定向了。
MAP遍历的方式
一、在键值都需要的时候使用
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
二、在for each循环中遍历keys和values
如果只需要map中的键或者值,可以通过keySet或values来实现遍历。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍历map中的键
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍历map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
三、使用iterator遍历。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
这种方式看起来冗余,却有其优点所在,首先在老版本Java中这是唯一遍历map的方式,同时可以在遍历map的同时使用iterator.remove()来删除数据。
四、通过键找值遍历
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
dubbo(※)
下次再问dubbo,一定给他吹出花来。
https://blog.51cto.com/13732225/2295896
调用关系说明:
1、服务容器负责启动、加载、运行服务提供者。
2、服务提供者在启动时,向注册中心注册自己提供的服务。
3、服务消费者在启动时,向注册中心订阅自己需要的服务。
4、注册中心提供服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
5、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选择另一台进行调用。
6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时没分组发送一次统计数据给监控中心。
官方推荐使用zookeeper注册中心,注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者与消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。
面试题:
1、dubbo中,zookeeper作为注册中心,当zookeeper挂掉时,发布者和消费者之间还能互相通信吗?
可以的,dubbo启动时,消费者会总zk拉取生产者的地址接口等数据,缓存在本地,每次调用时,按照本地存储的地址进行调用。注册中心对等集群,一台挂掉之后,会自动切换到另一台,注册中心全部宕机,服务提供者和消费者仍可以通过本地缓存进行通讯。服务提供者无状态,任一台宕机后,不影响使用。服务提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务提供者恢复。
zookeeper注册中心:
流程说明:
1、服务提供者启动时:向 /dubbo/com.foo.BarService/providers目录写下自己的URL地址。
2、服务消费者启动时:订阅/dubbo/com.foo.BarService/providers目录下服务提供者的URL地址,并向/dubbo/com.foo.BarService/consumers 目录写下自己的URL地址
3、监控中心启动时,订阅/dubbo/com.foo.BarService目录下所有的提供者和消费者URL地址。
支持以下功能:
- 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息。
- 当注册中心重启时,能自动恢复注册数据,以及订阅请求。
- 当会话过期时,能自动恢复注册数据,以及订阅请求。
- 当设置 <dubbo:registry check=“false” /> 时,记录失败注册和订阅请求,后台定时重试
- 可通过 <dubbo:registry username=“admin” password=“1234” /> 设置 zookeeper 登录信息
- 可通过 <dubbo:registry group=“dubbo” /> 设置 zookeeper 的根节点,不设置将使用无根树
- 支持 * 号通配符 <dubbo:reference group="" version="" />,可订阅服务的所有分组和所有版本的提供者
Redis的优缺点
为什么使用Redis,主要考虑性能与并发。
Redis的缺点:
1、缓存与数据库双写一致性问题。
一致性问题是分布式常见的问题,还可以分为最终一致性和强一致性。数据库和缓存双写,必然会存在数据不一致的情形。首先前提:如果要保证数据有强一致性要求,数据不能放缓存中,我们只能保证最终一致性。另外,我们所做的方案,从根本上来说只能降低不一致发生的概率,不能完全避免。
2、缓存雪崩问题。
即缓存同一时间内大面积失效,这时候又来了一批请求,全部到数据库上,从而导致数据库连接异常。
解决:
-
给缓存的失效时间加上一个随机值,避免集体失效。
-
使用互斥锁,但是该方案的吞吐量明显下降了。
-
双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点
I 从缓存A读数据库,有则直接返回
II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
III 更新线程同时更新缓存A和缓存B。
3、缓存击穿问题。
即黑客故意去请求缓存中不存在的数据,导致所有的请求都到数据库上,从而数据库连接异常。
解决:
- 采用互斥锁,缓存失效时,先去获得锁,得到锁之后再去请求数据库,没得到锁则休眠一段时间后重试。
- 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效的时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。
- 提供一个能迅速判断请求是否有效的拦截机制。
4、缓存的并发竞争问题。
如果对这个key操作不要求顺序,这种情况下准备一个分布式锁,大家去抢锁,抢到锁就做set操作。
如果对这个key的操作要求顺序:
假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC。期望key1的value值按照valueA,valueB,valueC的顺序去变化,这种时候在写入数据的时候,需要保存一个时间戳,如下
| 系统A | key1 | valueA 3:00 |
| 系统B | key1 | valueB 3:05 |
| 系统C | key1 | valueC 3:10 |
那么假设系统B先抢到锁,将key1设置为valueB 3:05,接下来系统A抢到锁,发现自己的valueA中的时间戳小于缓存中的时间戳,那么就不做set操作了。
其他方法,利用队列,将set方法变成串行访问也行。
单线程的Redis为什么这么快?
1、纯内存操作。
2、单线程操作,避免的频繁的上下文切换。
3、采用了非阻塞I/O多路复用机制。
Redis的五种基本数据类型:
1、String
2、list
3、set
4、hash
5、sorted set
MySql默认的锁,版本
数据库事务
事务就是逻辑上的一组sql语句操作,组成这组操作的各个sql语句,执行时要么全部成功要么全部失败。
事务的四大特性:
1、原子性
整个事务的所有操作,要么全部成功,要么全部不成功,不可能停滞在中间的某个环节。事务在执行过程中的某个环节出现错误会被回滚(rollback)到事务开始前的状态,就像这个事务没有执行过一样。
2、一致性
事务发生前和发生后,数据的完整性必须保持一致。
3、隔离性
多个并发事务之间的数据是相互隔离的。
4、持久性
一个事务一旦被提交,对数据库中数据的改变就是永久的,如果出现了错误,事务也不允许撤销。