批量操作: jdbc批量 redis批量
用户表用redis哈希映射
线程,线程池, 设计模式, 集合,map基础 http 协议 tcp 协议 读写分离 dubbo负载非均衡
redis 限速: 短信验证码: 已手机号作为key,用redis exists判断key是否存在, 不存在,用redis setex 设置key 并且设置有效期, 存在,用redis incr(key) 自增,判断返回值是否大于指定值。
限制接口访问2秒只能访问一次: 判断key是否存在, 存在:返回请求频繁 不存在:setex key 2000 vlaue, 处理业务
多线程: 5中状态:
新建状态(new):线程刚创建, 就绪状态(runnable):线程对象调用start()方法,进入就绪状态,等待cpu调度。 运行状态(running):cpu开始调度就绪状态的线程时,此时线程才是真正执行。 阻塞状态(blocked):线程暂时放弃cpu执行权,线程停止执行: 阻塞分为3种:1等待阻塞:运行中的线程执行wait()方法,线程进入阻塞,会释放锁。 2,同步阻塞:线程在获取synchronized同步锁失败时,会进入同步阻塞状态 3,睡眠阻塞:线程执行sleep()进入阻塞状态,当sleep()状态超时时,线程进入就绪状态,注:sleep 不会释放锁。 死亡状态:线程执行完毕。
线程执行的过程中,遇到synchroneize修饰,会尝试去获取锁,获取不到,就等待,等待其他线程释放这个锁。
创建线程一般2种方式: 1,继承Thread类,重写Thread类的run()方法。 然后用start()启动线程。
2,实现Runnable 接口,重写Runnable 的run方法。 Runnable myRunnable =new MyRunnalbe(); new Thread (myRunnable ).start();
然后用start()启动线程。
然后Thread和Runnable的区别: Thread不适合资源共享,实现Runnable方式很容易实现资源共享
Runnable 接口比继承Thread所具有的优势: 1,适合多个相同的程序的线程去处理同一个资源。 2,可以避免java中的单继承的限制。 3,代码可以被多个线程共享 4,线程池只能放入实现Runnable类的线程。
join () :指当前线程等待子线程执行完成之后,当前线程才能往下面执行。 yield():线程礼让,指当前线程让出执行权,但还可能继续抢执行权。 sleep():当前线程进入等待状态,但不让出执行权。 wait():进入等待状态,会释放锁。
sleep()和wait()区别: 共同点:都表示线程进入等待状态,不会立刻往下执行。 不同点:sleep是Thread的方法。 wait和notify是obj方法,锁对象调用。
sleep必须捕获异常,wait不用。 sleep睡眠时,保持对象锁,仍然占有该锁。 wait睡眠时,释放对象锁。 sleep可以是任意位置, wait必须是在同步锁代码块里,并且是用锁对象调用。
volatile
在多线程中,多个消费者对共享资源消费,我一般都是借助redis阻塞队列完成 就比如: 1个生产者,用redis list集合 lpush方法,往list集合左边添加元素。 然后多个消费者,从list集合右边阻塞弹出元素,使用brpop()方法,谁抢到元素,谁就拿去消费。
线程池:ThreadPoolTaskExecutor
线程池作用: 主要是为了减少不必要的开销,因为线程的创建和销毁是开销巨大,耗费性能。
ThreadPool 实现原理:
1,ThreadPool extend ThreadGroup ThreadPool 私有常量,是否关闭线程池。 ThreadPool 私有LinkedList<Runnable> workQueue 表示工作队列(任务队列)。
2,在ThreadPool 构造方法创建指定数量的线程,
public class ThreadPool extends ThreadGroup{ private LinkedList<Runnable> workQueue; //任务队列 public void ThreadPool(int poolSize){ workQueue =new ArrayList<Runnable>(); for(int i=0;i<poolSize;i++){ new WorkThread().start(); } }
//放任务进入任务队列准备执行 public void synchronized putTask(Runnable task){ workQueue.add(task); notify(); }
//从任务队列获取任务去执行 private Runnable synchronized getTask(){
if(workQueue.size()==0){ wait(); } return workQueue.removeFirst();
}
private class WorkThread extends Thread{ @override public void run(){ while(true){ //从任务列表里取出一个任务并执行 Runnbale task=getTask(); if(task!=null){ task.run(); } } } }
}
http协议面试题: http请求有三部分组成,分别是:请求行,消息报头,请求正文. http(超文本传输协议) 请求方法: get,post get与post: 1,get重点是从服务器获取资源,post重点是向服务器发送数据 2,get传输参数是拼接在请求url里的,用key=value形式,多个用&隔开,首个用?拼接,对用户是可见的。 post请求参数是放在请求实体,对用户是不可见的 3,get传输的数据量少,因为url有长度限制 post可以传输大量数据。 4,get是不安全的,可能会泄露私密信息, post较get安全点
5,get传输中文会乱码,因为只支持ascll字符。 post可以正确传输中文。
http与https 1,http通信使用明文不加密,内容可能被窃听 1,http请求不验证通信方身份,数据可能会被篡改,
https就是http加上加密处理(sll)+认证+完整性保护
https过程: 1客户端使用https的url访问web服务器,与web服务器建立ssl连接。 2web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。 3客户端的浏览器与web服务器开始协商ssl连接的安全等级,也就是信息加密等级。 4客户端的浏览器根据双方同意的安全等级,建立会话秘钥,然后利用网站的公钥将会话秘钥加密,并传给网站。 5web服务器利用自己的私有解密出会话密码。 6web服务器利用会话秘钥加密与客户端之间的通信。
1,客户端请求https连接,返回证书、公钥 2,客户端产生随机(对称)秘钥 3,客户端使用公钥对对称加密秘钥加密,然后发给服务端 4,通过对称秘钥加密密文通信。
设计模式: 一,单例:
1,直接定义静态私有变量 =new Object(); 在类初始化的时候就实例化对象
2,直接定义静态私有变量 =null; 然后在静态代码块里初始化对象。
3,调用getInstance获取对象时才初始化对象,
public static sychronized Object getInstance(){ if(instance==null){ instance =new Object(); } return instance;
}
4,双重校验同步锁
public static object getInstance(){ if(obj==null){ synchronized(Singleton.class){
if(obj==null){ obj=new Object(); } }
}
return obj;
}
5,静态内部类 public class Singleto{ private static class SingletonHolder{ private static final Singleton instance =new Singleton(;) } private Singleton(){ } public static final Singleton getInstance(){ return SingletonHolder.instance ; }
}
这种是利用classloder的机制来保证初始化instance时只有一个线程。
二装饰模式: 可以在不改变代码的情况下,增加功能。容易暴露真实对象 public class newClass implements IClass{ private IClass target;//真实对象,被包装的对象 public newClass(IClass target){ this.target=target; } public void save(){ //其他业务 //调用目标对象 tartget.save(); }
}
三代理模式: 1):静态代理:缺点:一个代理类只能代理一种类型; 和装饰设计模式相比,静态代理没有暴漏真实对象,更安全; 2):动态代理: jvm通过反射机制动态生成的动态代理类,代理对象和真实对象在程序运行时才确定 小结: 动态代理需要为每一个去创建一个代理对象; 最小代理的单位是类; 如果只想一个类型中的某些方法,需要在invoke中对method方法进行判断即可; 如果一个类实现了接口就用jdk动态代理;没有实现接口就用cglib动态代理;
a):jdk动态代理:需要实现接口
四策略模式: redis 工具类书写时,用策略模式, 本地 用单机板redis shardJedist 线上用用集群板的redis rediscluster
五模板方法, 父类定义大家公共的逻辑 不同逻辑留给子类去实现
spring 读写分离 spring 提供一个动态数据源AbstractRoutingDataSource,子类需要继承它,并且实现它的一个方法, 在spring 需要数据源的时候会调用这个方法获取数据源。
spring xml 配置主、从数据源,动态数据源, 动态数据源 有一个map 属性,需要指向主,从数据源。
在事务管理器数据源指向动态数据源。
配置一个aop 前置通知,根据切入方法打上的标签来选择数据源,选择之后放在ThreadLocal 变量里,在spring需要数据源的时候 回掉子类方法,从threadLocal线程变量里获取指定的数据源。
集合: Vector 和ArrayList的关系: 1底层算法都基于数组 2ArrayList新的变长数组,Vector是ArrayList的前身 3Vector 相对于ArrayList来说,线程更安全,但是性能较低
Stack栈:
底层是数组,特点:先进后出
push:压入栈顶 peek:弹出栈顶,不移除元素 pop:弹出栈顶,移除元素
集合:list queue set
list:ArrayList Vector LinkedList
list:记录添加顺序,元素可重复 ArrayList、Vector :底层是数组, LinkedList:底层是双向链表,会记录头和尾部
queue 底层是队列,先进先出
Set接口:不会记录添加顺序,元素不可以重复 set是通过equal() 和hashcode 保证唯一的
Dueue 双向队列
HashSet类:底层是哈希表算法 LinkedHashSet 类:双向链表和哈希表算法 LinkedhashSet 是hashset的子类 哈希表算法决定存储位置。 双向链表算法:决定输出顺序。
LinkedhashSet相对于hashset来说性能更低,因为要保证输出顺序。 LinkedhashSet 不仅可以保证添加顺序,还可以保证元素不能重复。
TreeSet :底层平衡二叉树 对象集合必须是同一种类型,不然会报错。
map 所有键值对:map.entrySet(); 所有键 :map.keySet() 所有值:values();
map 遍历: Map<Interger,Integer> map =new HashMap<Integer,Integer>(); for(Integer key:map.keySet()){ map.get(key);
}
for(Entry<Integer,Integer> entry:map.entrySet()){ entry.getKey(); entry.getValue();
}
Iterator<Integer> itegerto =map.keySet().iterator(); while(iterator.hasNext()){ map.get(key);
}
hashmap :线程不安全,方法不是同步的 , 允许有null的键和值,效率高一点, HashMap继承自AbstractMap类 hastable :线程安全, 方法是同步的, 不允许有null键和值,效率稍低, Hashtable继承自Dictionary类
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的, 在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
排序: 冒泡排序: /** * 冒泡排序 * 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 * 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 * 针对所有的元素重复以上的步骤,除了最后一个。 * 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 * @param numbers 需要排序的整型数组 */ public static void bubbleSort(int[] numbers) { int temp = 0; int size = numbers.length; for(int i = 0 ; i < size-1; i ++) { for(int j = 0 ;j < size-1-i ; j++) { if(numbers[j] > numbers[j+1]) //交换两数位置 { temp = numbers[j]; numbers[j] = numbers[j+1]; numbers[j+1] = temp; } } } }
选择排序: /** * 选择排序算法 * 在未排序序列中找到最小元素,存放到排序序列的起始位置 * 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。 * 以此类推,直到所有元素均排序完毕。 * @param numbers */ public static void selectSort(int[] numbers) { int size = numbers.length; //数组长度 int temp = 0 ; //中间变量 for(int i = 0 ; i < size ; i++) { int k = i; //待确定的位置 //选择出应该在第i个位置的数 for(int j = size -1 ; j > i ; j--) { if(numbers[j] < numbers[k]) { k = j; } } //交换两个数 temp = numbers[i]; numbers[i] = numbers[k]; numbers[k] = temp; } }
直播,有聊共同好友,抢红包, springmvc 流程, 自我介绍 复习一遍txt
直播在线人数:
直播开启直播时会创建一个消息队列频道,用户进直播时就订阅这个频道,接收消息。
用户进入直播 的时候,通过websoket连接服务器: 要做的事情: 1,以用户的会话id+直播id为消息队列名字,订阅一个该直播的消息队列频道。 2,用redis 计数器 incrby 计数这个直播在线人数加1. 3,通过用户会话id 找出该用户登录信息(redist),把用户id存进redis在线用户列表set集合里(sadd())。 4,往该直播消息队列频道发一条消息:目前最新的在线人数,其他用户收到订阅消息就通过websoket发给前端。
用户退出直播(websoket断开): 1,直播计数器 redis decrby 减1, 2, 直播在线用户列表 redis set 集合剔除该用户id srem(); 3,往这个直播消息队列频道发一条消息,当前最新的在线人数,客户端收到就更新在线人数。
有聊共同好友: 用户关系表 friends
userId: friendid:
已知条件是我的id,你的id
select * from friends f1 ,friends f2 where f1.userid=myuserid and f2.userid=youid and f1.friendid=f2.friendid
聊天群组抢红包: 红包发起: 红包发起的时候前端传给我的数据是:一个红包金额,红包数量,拆分规则:红包金额是否随机。 然后我就把这个红包当做一个大红包保存到数据库,就有一个大红包id。 把这个大红包id 缓存到redis 中, 大红包id作为key 有效时间24小时。 然后把这个这个大红包按照红包拆分规则 拆成一个个小的红包,保存到数据库里面。 然后把这些小红包id 保存到redis set 集合里。 然后往群组发一条红包消息。
红包领取: 1,首先判断这个大红包是否过去,用redist exist 判断key是否存在,如果存在那说明没过期,不存在过期了。
2,如果没过期的话,再判断这个用户是否重复抢这个红包,用redis setnx分布式锁机制,大红包id+用户id作为key,key有效时间大于大红包的有效时间。 如果setnx 成功那说明他没抢, 3,没抢的话,就从小红包列表 set 集合 spop随机弹出一个红包,把用户id和小红包id进行绑定。
事务隔离级别: 1,读未提交:别人事务还没提交,我就可以读取到。 2,不可重复读:同一个事务中,前后两次读取到的数据不一样。 3,可重复读:同一个事务中,前后两次读取到的数据一样。 4 串行化:事务排队
jvm 栈,堆,静态区
堆: 1,new 出来的对象存在堆区,堆区不存放基本数据类型和对象引用,只存放对象本身。 2,jvm 只有一个堆区,堆区被所有线程共享。 3,运行时动态分配内存,存取速度慢,内存数据由java垃圾收集器自动回收。
栈: 1,每个线程包含一个栈区,栈区只保存基本数据类型和对象的引用。 2,每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 3,栈存取速度比堆快,
方法区: 1又叫静态区,跟堆一样,被所有线程共享,方法区包含所有的class和static变量。 2方法区中包含的都是整个程序中永远唯一的元素,如class,static 变量。
spring mvc 流程 1,用户发送请求到前端控制器DispatcherServlet. 2,DispatcherServlet收到请求调用HandlerMapping处理器映射器。 3,处理器映射器找到具体的处理器(可以根据xml,注解进行查找),生成处理器对象及如果有拦截器的话也一起生成,然后返回给dispatcherServlet 4,dispatcherservlet 调用handlerAdapter 处理适配器。 5,HandlerAdapter经过适配之后调用具体的后端控制器,就是后端的controller, 6,controller执行完成之后返回modelAndview 给handlerAdapter适配器。 7,handlerAdapter适配器就将controller执行结果modelAndview返回给dispatcherservlet 8,dispatcherServlet将modelAndview 传给viewRelover视图解析器。 9,viewReslove解析后返回具体view给dispatcherServlet 10,dispatcherServlet根据view渲染视图,相应给用户。
MySQL in
select * from table1 pc where pc.account_id in (45); select * from table1 pc where (pc.account_id) in ((45)); select * from table1 pc where (pc.account_id,pc.app_id) in ((45,'in123456789'));