1.java基本数据类型
java一共8个基本数据类型
byte 1字节(1byte = 8 bit)
short 2字节
int 4字节
long 8字节
double 8字节
char 2字节(C语言中是1字节)可以存储一个汉字
float 4字节
boolean false/true(理论上占用1bit,1/8字节,实际处理按1byte处理)
(string 4字节 不是基本数据类型)
2.Set、List、Map的区别和联系
Set和List都是继承自Collection接口,Map不是
Set
(1)不允许重复对象
(2)无序容器(你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。)
(3)只允许有一个null元素
(4)Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
List(接口)
(1)允许重复对象
(2)有序容器
(3)可以插入多个null元素
(4)常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
Map
(1)适用于存储键值对
(2)可随意创建null值,但只能有一个null键
(3)Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
<1>线程安全集合类与非线程安全集合类
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;
HashMap是非线程安全的,HashTable是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。
(a)克隆是把一个对象里面的属性值,复制给另一个对象。而不是对象引用的复制
(b)将对象的状态保存在存储媒体中一边可以在以后重写创建出完全相同的副本
按值将对象从一个应用程序域法相另一个应用程序域实现Serializable接口的作用就是可以把对象存到字节流,然后可以恢复。所以你想你的对象没有序列化,怎么才能在网络传输呢?要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果你不需要分布式应用,那就没必要实现序列化
3.Arrays.sort的实现
参考:
(1) http://blog.csdn.net/u0/article/details/?locationnum=6&fps=1
(2) http://blog.csdn.net/wisgood/article/details/
4.什么时候使用CopyOnArrayList
CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里(多线程),比如缓存。发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主的情况
5.volatile的使用
互斥性 即一次只允许一个线程持有某个特定的锁,从而保证一次就只有一个线程能够使用该共享数据。
可见性 必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 (如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。)
参考:http://blog.csdn.net/jinfeiteng2008/article/details/
6.synchronied的使用
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
(1) 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
(2) 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
(3) 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
参考:http://blog.csdn.net/luoweifu/article/details/
7.CAS的实现原理以及问题
一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。
拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。
首先毫无以为,在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。
这样才获取变量的值的时候才能直接读取。
然后来看看++i是怎么做到的。
在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。
而compareAndSet利用JNI来完成CPU指令的操作。
锁机制存在以下问题:
(1) 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
(2) 一个线程持有锁会导致其它所有需要此锁的java基础高级知识点线程挂起。
(3) 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
参考:http://blog.csdn.net/u0/article/details/
8.接口和抽象类的区别,什么时候使用
(1) abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
(2)在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
(3) abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。
(4) 实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
(5) 接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
(6) 抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
(7) 接口中的方法默认都是 public,abstract 类型的。
9.JVM类加载机制
(1) 装载:查找和导入Class文件;
四个类加载器:
(a) Bootstrap ClassLoader是在JVM开始运行的时候加载java的核心类
(b) Extension ClassLoader是用来加载扩展类,即/lib/ext中的类
(c) AppClassLoader用来加载Classpath的类,是和我们关系最密切的类
(d) URLClassLoader用来加载网络上远程的类
(2) 链接:把类的二进制数据合并到JRE中;
(a)校验:检查载入Class文件数据的正确性;
(b)准备:给类的静态变量分配存储空间;
(c)解析:将符号引用转成直接引用;
(3) 初始化:对类的静态变量,静态代码块执行初始化操作
参考:http://blog.csdn.net/fgets/article/details/
10.Spring注解
(2) @RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
如:
(4) @ModelAttribute和 @SessionAttributes
(5) @PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
如:
(7) @ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
参考:https://www.cnblogs.com/leskang/p/5445698.html
11.反射机制
反射机制是java的动态性之一
动态语言是指 程序在运行时可以改变其结构,新的函数可以引进,已有的函数可以被删除等结构上的变化,如javascript,python
Java通过反射机制,可以在程序运行时加载,探知和使用编译期间完全未知的类,并且可以生成相关类对象实例,从而可以调用其方法或则改变某个属性值。所以JAVA也可以算得上是一个半动态的语言。
参考:http://blog.csdn.net/xu__cg/article/details/
12.JVM内存管理机制
13.JVM垃圾回收机制
堆(heap) : 他是最大的一块区域,用于存放对象实例和数组,是全局共享的.
栈(stack) : 全称为虚拟机栈,主要存储基本数据类型,以及对象的引用,私有线程
方法区(Method Area) : 在class被加载后的一些信息 如常量,静态常量这些被放在这里,在Hotspot里面我们将它称之为永生代
新生代、老年代
(1) 作用对象:超出了作用域或引用计数为空的对象;从gc root开始搜索找不到的对象,而且经过一次标记、清理,仍然没有复活的对象。
(2) 作用时间:eden满了minor gc,升到老年代的对象大于老年代剩余空间full gc,或者小于时被HandlePromotionFailure参数强制full gc;gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM,调优诸如通过NewRatio控制新生代老年代比
(复制算法:将区域分成两部分,其中一部分作为保留空间,另一部分作为使用空间、当发生时,首先检查使用空间里有哪些对象是存活的,检查完之后把存活的对象复制到保留空间(这样复制过来的好处是减少了内存碎片,如果直接在使用空间清除的话,那空间会很零散)里,然后清洗使用空间。
这个eden就相当于是使用空间,survivor就相当于是保留空间,通常情况下eden会比survivor大的多,因为eden和survivor都是属于新生代(还有老生代,jvm 将堆分为新生代和老生代),新生代里的对象一般都是[朝生夕死],所以活下来的不多,所以保留空间小一些就好了)
参考:http://blog.csdn.net/cy/article/details/
14.内存溢出和内存泄露
(1) 内存泄漏:指程序申请内存后,无法释放
(3)内存溢出原因:
(a) 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
(b) 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
(c) 代码中存在死循环或循环产生过多重复的对象实体;
(d) 使用的第三方软件中的BUG;
(e) 启动参数内存值设定的过小
15.Redis和memcached
redis:Redis是一个开源的key-value存储系统。与Memcached类似,Redis将大部分数据存储在内存中,支持的数据类型包括:字符串、哈希表、链表、集合、有序集合以及基于这些数据类型的相关操作。
memchached:高性能分布式内存缓存服务器。其本质上就是一个内存key-value数据库,但是不支持数据的持久化,服务器关闭之后数据全部丢失。
(2) 内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
(3) 性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
redis使用:
Redis配置
参考:https://www.cnblogs.com/zxtceq/p/7676911.html
16.MySQL的读写分离
可通过添加时间戳字段t,update时where t=XXX,匹配不到说明该条记录已被修改,需重新查找该条记录并带入新的t值;若有主从不可在sql中更新t=now(),因为主从数据库存在时间差,会导致数据不一致,需在java中带入t值
17.MySQL的主从数据库
(1) 主从搭建
参考:https://www.jianshu.com/p/99f8aaa82f96
(2) 保证一致性(若检测到数据库数据不一致)
(a) 直接在主库上将数据mysqlkdump出来,然后再灌进从库。但是这样做有一个缺陷,如果在你dump出数据和灌入slave的过程中master的这张表又发生了变化怎么办呢?而且有时候这样做的代价可能会很高,比如有一个壹佰万行的表上只有一行数据不一样。
(b) percona toolkit的一个工具叫pt-table-sync可以解决上面的问题。
参照:http://blog.sina.com.cn/s/blog_c2839d2a0102wl54.html
18.sql的优化
参考:http://blog.csdn.net/jie_liang/article/details/
19.http请求/响应报文结构
HTTP请求报文
一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据。
(1) 请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1
(2) 请求头部
HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者 POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说 Content-Length必须出现
(3) 空行
它的作用是通过一个空行,告诉服务器请求头部到此为止。
(4) 请求数据
若方法字段是GET,则此项为空,没有数据
若方法字段是POST,则通常来说此处放置的就是要提交的数据
HTTP响应报文
同样的,HTTP响应报文也由三部分组成:响应行、响应头、响应体
(1) 响应行
响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK
其中协议版本HTTP/1.1或者HTTP/1.0,200就是它的状态码,OK则为它的描述。
常用404(意味着你请求的资源在web服务器中没有)403(服务器拒绝访问,权限不够)
500~599:服务器端出现错误,常用500
(2) 响应头
响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。
(3) 响应体
响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。
20.http三次握手和四次挥手
TCP(Transmission Control Protocol) 传输控制协议
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
参考:http://blog.csdn.net/yanxinrui0027/article/details/
21.Linux常用命令
22.深入理解String、StringBuffrt、StringBuilder
(1) 三者在执行速度方面的比较:StringBuilder > StringBuffer > String
(2) StringBuffer做了同步处理,是线程安全的,StringBuilder是非线程安全的。
(3) String和StringBuffer主要有2个区别:
(a) String类对象为不可变对象,一旦修改了String对象的值,隐性重新创建了一个新的对象,释放原String对象,StringBuffer类对象为可修改对象,可以通过append()方法来修改值
(b) String类的性能远不如StringBuffer类。
(4) String:
(a) 是对象不是原始类型,是引用类型。
(b) String 是final类,不能被继承,一旦被创建,就不能修改它的值.
(c) 底层用char[]来实现。
(d) 在用”+”进行字符串连接的时候,底层是新建一个String对象,通过新建一个StringBuilder或StringBuffer对象,调用其append方法,然后调用toString方法(在调用toString方法的时候会再创建一个String对象),返回给新建的String对象。其中会频繁的创建新对象,增加了虚拟机GC的工作量,频繁字符串连接的时候不推荐使用。
(5)StringBuffer:
(a) 是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象。
(b) 底层用char[]来实现。
(c) 它只能通过构造函数来创建:
(d) 对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer中赋值的时候可以通过它的append()方法.
sb.append("hello");
在调用append()方法的时候会先判断StringBuffer底层char[]的长度,如果长度不够用,就对char[]进行扩展,新长度为原来长度的2倍+2。
(6) 总结
(a) 在字符串连接操作中StringBuffer的效率要比String高。
(b) 如果没有频繁的字符串连接,可以用String,如果有频繁的字符串连接,推荐用StringBuffer(线程安全)或者StringBuilder(非线程安全)。
(c) StringBuffer之间的比较,要先调用toString()方法,再调用equals()方法作比较。
23.redis和mysql的同步问题
方法1:
mysql 同步到redis:解析mysql的binlog,然后做同步处理,可以使用的库有:open-replicator(https://github.com/whitesock/open-replicator)
方法2:
同步redis数据到mysql:(https://github.com/leonchen83/redis-replicator)
redis读取速度快,也没有必要把所有的数据都放到redis里面,redis里面只放使用频繁,用户操作量较大的数据,或者用户近期使用的数据。解决办法:
(a) 读取数据的时候先从redis里面查,若没有,再去数据库查,同时写到redis里面,并且要设置失效时间。
(b) 存数据的时候要具体情况具体分析,可以选择同时插到数据库和redis(要是存放到redis中,最好设置失效时间),也可以选择直接插到数据库里面,少考虑一些问题。
参考:https://blog.csdn.net/Luomingkui1109/article/details/
24.线程池参数问题
线程池的实现ThreadPoolExecutor
(1) corePoolSize
核心池的大小
(3) keepAliveTime
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit keepAliveTime的单位
(5) workQueue
三种队列
如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放添加到queue中,而是直接抄家伙(thread)开始运行)
如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
(a) 直接提交 SynchronousQueue
(b) 无界队列 LinkedBlockingQueue
(c) 有界队列 ArrayBlockingQueue
参考:https://www.oschina.net/question/_86540
(6)4种策略
(a)CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
(b)DiscardPolicy:不能执行的任务将被删除
(c)AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException
(d)DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
线程池按以下行为执行任务
(1) 当线程数小于核心线程数时,创建线程。
(2) 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
(3) 当线程数大于等于核心线程数,且任务队列已满
(a) 若线程数小于最大线程数,创建线程
(b) 若线程数等于最大线程数,抛出异常,拒绝任务
例子:
假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。
因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;
当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;
如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招4个临时工人进来;
然后就将任务也分配给这4个临时工人做;
如果说着14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。
当这14个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉4个临时工了,只保持原来的10个工人,毕竟请额外的工人是要花钱的。
这个例子中的corePoolSize就是10,而maximumPoolSize就是
14(10+4)
参考:https://www.2cto.com/kf/201711/695731.html
25.生产者消费者代码实现
参考:https://www.cnblogs.com/Ming8006/p/7243858.html
26.死锁代码实现
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
27.SpringMVC和Spring boot
(1) Spring MVC
(2) Spring Boot
Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。
对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。
所以,用最简练的语言概括就是:
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。
28.Spring boot实例创建
参考:https://www.cnblogs.com/moonlightL/p/7891803.html
29.动态绑定
那些在类被加载时就已经知道(final、private、static修饰的方法以及构造函数),不需对象的创建就能访问的,就是静态绑定的内容;需要等对象创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容。
方法表:表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处
参考:https://www.cnblogs.com/ygj0930/p/6554103.html
30.SpringMVC深入
spring工作原理:
(1)spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。
(2)DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller.
(3)DispatcherServlet请请求提交到目标Controller
(4)Controller进行业务逻辑处理后,会返回一个ModelAndView
(5)Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
(6)视图对象负责渲染返回给客户端。
参考:https://www.cnblogs.com/angelye/p/7506566.html
参考:https://www.cnblogs.com/baiduligang/p/4247164.html
31.事务
(1) 原子性:事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
(2) 一致性:事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
(3) 隔离性:在一个事务提交之前,对其它事务不可见;
(4) 持久性:一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改。
7种传播级别:
l PROPAGATION_REQUIRED
默认的Spring事务传播级别,使用该级别的特点是:如果上下文中已经存在事务,那么就加入到事务中执行;如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
l PROPAGATION_SUPPORTS
从字面意思就知道,supports,支持,该传播级别的特点是:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包含在TransactionTemplate.execute方法中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
l PROPAGATION_MANDATORY
该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
l PROPAGATION_REQUIRES_NEW
从字面即可知道,new,每次都要一个新事务,该传播级别的特点是:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。
怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。
l PROPAGATION_NOT_SUPPORTED
这个也可以从字面得知,not supported,不支持,当前级别的特点是:若上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
这个级别有什么好处?可以帮助你将事务尽可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况,所以事务的这个传播级别就派上用场了。用当前级别的事务模板包含起来就可以了。
l PROPAGATION_NEVER
该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
l PROPAGATION_NESTED
从字面也可知道,nested,嵌套级别事务。该传播级别的特征是:如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
4种事务的隔离级别
l SERIALIZABLE
最严格的级别,事务串行执行,资源消耗最大。
l REPEATABLE_READ
保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
l READ_COMMITTED
大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
l READ_UNCOMMITTED
保证了读取过程中不会读取到非法数据。
一些术语
l 脏读(Dirty Reads)
所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
l 不可重复读(Non-RepeatableReads)
不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
l 幻读
小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?产生幻觉了?
幻读也是这样子,事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。
32.zookeeper
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户
Zookeeper的特点
(1) 最终一致性:为客户端展示同一视图,这是zookeeper最重要的功能。
(2) 可靠性:如果消息被到一台服务器接受,那么它将被所有的服务器接受。
(3) 实时性:Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
(4) 等待无关(wait-free):慢的或者失效的client不干预快速的client的请求。
(5) 原子性:更新只能成功或者失败,没有中间状态。
(6) 顺序性:所有Server,同一消息发布顺序一致。
Zookeeper工作原理
Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
33.阻塞线程
sleep()
join()
yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
wait()和notify()、notifyAll()
协调多个线程对共享数据的存取,必须在synchronized语句块内使用
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
34.线程run和start
run(); 只是调用了一个普通方法,并没有启动另一个线程,程序还是会按照顺序执行相应的代码。
start(); 则表示,重新开启一个线程,不必等待其他线程运行完,只要得到cup就可以运行该线程。
把需要并行处理的代码放在run() 中,start()启动线程将自动调run(),这是jvm机制决定的,并且run()方法必须是public 访问权限,然会类型为void
35.DispatcherServlet
主要用作职责调度工作,本身主要用于控制流程,主要职责如下:
1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
4、通过ViewResolver解析逻辑视图名到具体视图实现;
5、本地化解析;
6、渲染具体的视图等;
7、如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
36.dubbo+zookeeper分布式
参考:https://blog.csdn.net/noaman_wgs/article/details/
37.ibatis和Mybatis
38.拦截器和过滤器的区别
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
39.重定向(redirect)和转发(forward)
转发(forward)
redirect(重定向):
40.java算法
参考:https://blog.csdn.net/l/article/details//
41.java设计模式
共23种
(1) 单例模式
(2) 工厂模式
(3) 生产者消费者模式
(4) 观察者模式
(5) 适配器模式
(6) 代理模式
参考:https://www.cnblogs.com/cr330326/p/5627658.html
https://blog.csdn.net/a/article/details/
42.java性能优化
JVM
sql
代码
gc
Java开发高并发的处理方法:
最基础的地方做起,优化我们写的代码,减少必要的资源浪费
避免频繁的使用new对象,对于整个应用只需要存在一个实例的类,我们可以使用单例模式。对于String连接操作,使用 StringBuffer或StringBuilder,对于工具类可以通过静态方法来访问。
避免使用错误的方式,尽量不用instanceof做条件判断。使用java中效率高的类,比如ArrayList比Vector性能好。
图片服务器分离
对于web服务器来说,图片是最消耗资源的,于是我们有必要把图片与页面进行分离,我们把图片放到独立的图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片的问题而崩溃。在图片服务器上,我们可以对不同的配置进行优化。
缓存
具体接触过的缓存机制是hibernate的缓存机制。为了避免每次都向数据库中取得数据,我们把用户常常访问到的数据放到内存中,甚至缓存十分大的时候我们可以把内存中的缓存放到硬盘中。还有高级的分布式缓存数据库使用,都可以增加系统的抗压力。
分批传送
在做某项目的时候,一次传递的参数太多,而且数据库规定一次最多传递的参数最多是三万条,当时有五万条记录,那怎么传送呢?最终是分批传送,电梯里一次乘不下那么多的人,会报超重的bug,那就分批把人送上去。
还有一次在考试系统中,如果那么多的考试人员同时提交到数据库中,数据库的压力增大,有时会被down掉,当时采用的方法是使用ajax异步传输,没有等待考生点击提交按钮的时候,就把考生的答案自动提交,这样也避免了突然断电考生前面做过的题出现丢失的现象。
DB优化
(a)索引的建立:建立索引要适当,如果一个表经常用来被查询,对于增加和修改很少被用到,我们就可以为这个表建立索引,因为对于增加和修改和删除操作时,我们对索引的维护要大大超过索引给我们带来的效率。
(b)表字段的类型选择要恰当。包括字段的长度、类型等,要根据实际存储的数据进行选择,长度不要过长,否则会影响效率。
(c)外键要慎用,因为主键代表这一张表,而外键代表一群表,对表之间进行了关联,在删除修改等需要我们关联。
(d)在数据库操作上。 尽量使用prepareStatement,少用Statement,因为PrepareStatement是进行预编译的。
(f)connection设置为readOnly,Connection是对书库连接,属于重量级,我们使用即可。
43.多线程同步
(1)同步方法:synchronized关键字修饰的方法
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
(2)同步代码块 :synchronized关键字修饰的语句块
(3)用特殊域变量(volatile)实现线程同步
(a) volatile关键字为域变量的访问提供了一种免锁机制,
(b) 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
(c) 因此每次使用该域就要重新计算,而不是使用寄存器中的值
(d) volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
(4)使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
注:关于Lock对象和synchronized关键字的选择:
(a) 最好两个都不用,使用一种java.util.concurrent包提供的机制,
能够帮助用户处理所有与锁相关的代码。
(b) 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
(c) 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
注:ThreadLocal与同步机制
(a) ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
(b) 前者采用以”空间换时间”的方法,后者采用以”时间换空间”的方式
44.数据库索引
创建索引:
(a) 普通索引:CREATE INDEX username ON mytable(username);
(b) 唯一索引:CREATE UNIQUE INDEX age ON mytable(age);
(唯一索引和主键索引与普通索引的区别是唯一,不重复。列值唯一,但是唯一索引可以有空值)
(c) 主键索引:ALTER TABLE mytable ADD PRIMARY KEY (id);
删除索引:
DROP INDEX 索引的名字 ON 索引的表;
组合索引
ALTER TABLE mytable ADD INDEX name_city_age (username,city,age);
12 .尽量的扩展索引,不要新建索引。
比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
注意:选择索引的最终目的是为了使查询的速度变快。上面给出的原则是最基本的准则,但不能拘泥于上面的准则。读者要在以后的学习和工作中进行不断的实践。根据应用的实际情况进行分析和判断,选择最合适的索引方式。
45.主从数据库怎么同步
从库通过监控主库二进制操作文件,拷贝到从库中继文件,然后执行同步二进制文件
该过程的第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,master通知存储引擎提交事务。 下一步就是slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。 SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。 此外,在master中也有一个工作线程:和其它MySQL的连接一样,slave在master中打开一个连接也会使得master开始一个线程。复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作
46.list、map的数据结构
集合
Collection(单列集合)
List(有序,可重复)
ArrayList底层数据结构是数组,查询快,增删慢线程不安全,效率高Vector底层数据结构是数组,查询快,增删慢线程安全,效率低LinkedList底层数据结构是链表,查询慢,增删快线程不安全,效率高Set(无序,唯一)
HashSet底层数据结构是哈希表。哈希表依赖两个方法:hashCode()和equals()执行顺序:首先判断hashCode()值是否相同是:继续执行equals(),看其返回值是true:说明元素重复,不添加是false:就直接添加到集合否:就直接添加到集合最终:自动生成hashCode()和equals()即可LinkedHashSet底层数据结构由链表和哈希表组成。由链表保证元素有序。由哈希表保证元素唯一。
TreeSet底层数据结构是红黑树。(是一种自平衡的二叉树)如何保证元素唯一性呢?
HashMap底层数据结构是哈希表。线程不安全,效率高哈希表依赖两个方法:
hashCode()和equals()执行顺序:首先判断hashCode()值是否相同是:继续执行equals(),看其返回值是true:说明元素重复,不添加是false:就直接添加到集合否:就直接添加到集合最终:自动生成hashCode()和equals()即可LinkedHashMap底层数据结构由链表和哈希表组成。由链表保证元素有序。由哈希表保证元素唯一。
Hashtable底层数据结构是哈希表。线程安全,效率低哈希表依赖两个方法:
hashCode()和equals()执行顺序:首先判断hashCode()值是否相同是:继续执行equals(),看其返回值是true:说明元素重复,不添加是false:就直接添加到集合否:就直接添加到集合最终:自动生成hashCode()和equals()即可TreeMap底层数据结构是红黑树。(是一种自平衡的二叉树)如何保证元素唯一性呢?根据比较的返回值是否是0来决定如何保证元素的排序呢?两种方式自然排序(元素具备比较性)让元素所属的类实现Comparable接口比较器排序(集合具备比较性)让集合接收一个Comparator的实现类对象
2:到底使用那种集合(自己补齐)看需求。
4:ArrayList,LinkedList,HashSet,HashMap(掌握)存储字符串和自定义对象数据并遍历5:集合的嵌套遍历(理解)
注:
1.其中的Arralist 代码中大量的用了System.arraycopy () 方法 进行数组进行复制
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
47.控制反转IOC和依赖注入DI
Spring所倡导的是所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI来实现的
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/19157.html