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是线程安全的。
<2>为什么Set、List、map不实现Cloneable和Serializable接口
克隆(cloning)或者序列化(serialization)的语义和含义是跟具体的实现相关的。因此应该由集合类的具体实现类来决定如何被克隆或者序列化
(a)克隆是把一个对象里面的属性值,复制给另一个对象。而不是对象引用的复制
(b)将对象的状态保存在存储媒体中一边可以在以后重写创建出完全相同的副本
按值将对象从一个应用程序域法相另一个应用程序域实现Serializable接口的作用就是可以把对象存到字节流,然后可以恢复。所以你想你的对象没有序列化,怎么才能在网络传输呢?要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果你不需要分布式应用,那就没必要实现序列化
3.Arrays.sort的实现
参考:
(1) http://blog.csdn.net/u011410529/article/details/56668545?locationnum=6&fps=1
(2) http://blog.csdn.net/wisgood/article/details/16541013
4.什么时候使用CopyOnArrayList
CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里(多线程),比如缓存。发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主的情况
5.volatile的使用
volatitle变量提供了线程的可见性,并不保证线程的安全性和原子性
线程的两个主要特性分别是互斥和可见性,
互斥性 即一次只允许一个线程持有某个特定的锁,从而保证一次就只有一个线程能够使用该共享数据。
可见性 必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 (如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。)
参考:http://blog.csdn.net/jinfeiteng2008/article/details/53423858
6.synchronied的使用
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
(1) 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
(2) 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
(3) 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
参考:http://blog.csdn.net/luoweifu/article/details/46613015
7.CAS的实现原理以及问题
一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。
拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。
private volatile int value;
首先毫无以为,在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。
这样才获取变量的值的时候才能直接读取。
public final int get() {
return value;
}
然后来看看++i是怎么做到的。
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。
而compareAndSet利用JNI来完成CPU指令的操作。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
锁机制存在以下问题:
(1) 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
(2) 一个线程持有锁会导致其它所有需要此锁的线程挂起。
(3) 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。
独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
CAS应用
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
参考:http://blog.csdn.net/u014082714/article/details/50825597
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/52934178
10.Spring注解
(1) @Controller
控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。
(2) @RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
如:
@RequestMapping(value = "/web/staff/order/list",
method = RequestMethod.POST,
produces = "application/json")
(3) @Resource和@Autowired
@Resource(byType)和@Autowired(byName)都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
(4) @ModelAttribute和 @SessionAttributes
(5) @PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
如:
@RequestMapping("/list/{userId}/{userType}")
public String searchOrder(Model model, OrderSearchParam orderSearchParam, @PathVariable("userId") String userId, @PathVariable("userType") String userType) {...}
(6) @requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter("name"),它有三个常用参数:defaultValue = "0", required = false, value = "isApp";defaultValue 表示设置默认值,required 铜过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
(7) @ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
(8) @Component
这是一个元注解,意思是它可以用于标注其他注解,被它标注的注解和它起到相同或者类似的作用
(9) @Repository
用于注解dao层,在daoImpl类上面注解。
参考:https://www.cnblogs.com/leskang/p/5445698.html
11.反射机制
反射机制是java的动态性之一
动态语言是指 程序在运行时可以改变其结构,新的函数可以引进,已有的函数可以被删除等结构上的变化,如javascript,python
Java通过反射机制,可以在程序运行时加载,探知和使用编译期间完全未知的类,并且可以生成相关类对象实例,从而可以调用其方法或则改变某个属性值。所以JAVA也可以算得上是一个半动态的语言。
参考:http://blog.csdn.net/xu__cg/article/details/52882023
12.JVM内存管理机制
(1) 方法区
方法区存放了要加载的类的信息(如类名,修饰符)、类中的静态变量、final定义的常量、类中的field、方法信息
(2)堆区
在JVM所管理的内存中,堆区是最大的一块,堆区也是JavaGC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区用来存储对象实例及数组值,可以认为java中所有通过new创建的对象都在此分配。
(3)本地方法栈
用于支持native方法的执行,存储了每个native方法调用的状态。本地方法栈和虚拟机方法栈运行机制一致,它们唯一的区别就是,虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
(4)虚拟机栈
占用的是操作系统内存,每个线程都对应着一个虚拟机栈,它是线程私有的,而且分配非常高效。一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
(5)程序计算器
一个比较小的内存区域,可能是CPU寄存器或者操作系统内存,其主要用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。
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/cy609329119/article/details/51771953
14.内存溢出和内存泄露
(1) 内存泄漏:指程序申请内存后,无法释放
(2) 内存溢出(OOM):指程序申请内存时,没有足够的内存供调用
(内存泄漏的堆积最终会导致内存溢出)
(3)内存溢出原因:
(a) 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
(b) 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
(c) 代码中存在死循环或循环产生过多重复的对象实体;
(d) 使用的第三方软件中的BUG;
(e) 启动参数内存值设定的过小
15.Redis和memcached
redis:Redis是一个开源的key-value存储系统。与Memcached类似,Redis将大部分数据存储在内存中,支持的数据类型包括:字符串、哈希表、链表、集合、有序集合以及基于这些数据类型的相关操作。
memchached:高性能分布式内存缓存服务器。其本质上就是一个内存key-value数据库,但是不支持数据的持久化,服务器关闭之后数据全部丢失。
比较:
(1) Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
(2) 内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
(3) 性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
redis使用:
public abstract class BaseRedisDao {
/**
* logger
*/
private static Logger logger = LoggerFactory.getLogger(BaseRedisDao.class);
/**
* Redis Save
*
* @param key Redis key
* @param value Redis value
* @return true保存成功false保存失败
*/
public boolean save(final String key, final String value) {
boolean result = getRedisTemplate().execute(new RedisCallback() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
RedisSerializer serializer = getRedisSerializer();
byte[] byteKey = serializer.serialize(key);
byte[] byteValue = serializer.serialize(value);
return connection.setNX(byteKey, byteValue);
}
});
return result;
}
}
Redis配置
# Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
# 启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis.pid
daemonize no
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
# 指定Redis监听端口,默认端口为6379
# 如果指定0端口,表示Redis不监听TCP连接
port 6379
# 绑定的主机地址
# 你可以绑定单一接口,如果没有绑定,所有接口都会监听到来的连接
# bind 127.0.0.1
# 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 0
# 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
# debug (很多信息, 对开发/测试比较有用)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel verbose
# 日志记录方式,默认为标准输出,如果配置为redis为守护进程方式运行,而这里又配置为标准输出,则日志将会发送给/dev/null
logfile stdout
# 设置数据库的数量,默认数据库为0,可以使用select 命令在连接上指定数据库id
# dbid是从0到‘databases’-1的数目
databases 16
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# Save the DB on disk:
#
# 满足以下条件将会同步数据:
# 900秒(15分钟)内有1个更改
# 300秒(5分钟)内有10个更改
# 60秒内有10000个更改
# Note: 可以把所有“save”行注释掉,这样就取消同步操作了
#save 900 1
save 300 10
#save 60 10000
# 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
# 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
# 工作目录.
# 指定本地数据库存放目录,文件名由上一个dbfilename配置项指定
# 注意,这里只能指定一个目录,不能指定文件名
dir ./
# 主从复制。使用slaveof从 Redis服务器复制一个Redis实例。注意,该配置仅限于当前slave有效
# 设置当本机为slav服务时,设置master服务的ip地址及端口,在Redis启动时,它会自动从master进行数据同步
# slaveof
# 当master服务设置了密码保护时,slav服务连接master的密码
# 下文的“requirepass”配置项可以指定密码
# masterauth
# 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过auth 命令提供密码,默认关闭
# requirepass foobared
# 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,
# 如果设置maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max Number of clients reached错误信息
# maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,
# 当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。
# Redis新的vm机制,会把Key存放内存,Value会存放在swap区
# maxmemory
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。
# 因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
...
参考: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/77340905
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/70243665
21.Linux常用命令
//查看java进程
ps -ef|grep java
//启动服务
nohup java -jar sostar-1.0.jar >console.log&
//查看日志文件
tail -f console.log
//卸载XXX包
adb uninstall com.house365.xiaomifeng
//查找占用
netstat -aon | findstr "5037"
//查看占用pid的进程名称
tasklist | findstr "6128"
//杀进程
taskkill /f /t /javaw.exe
//macos解除安全
sudo spctl --master-disable
//jar包运行
nohup java -jar sostar-1.0.jar --spring.profiles.active=test >console.log &
nohup java -jar sostar-1.0.jar --spring.profiles.active=pro>console.log &
nohup java -jar -Xms6144m -Xmx6144m -Xmn1536m sostar-1.0.jar --spring.profiles.active=pro>console.log&
//Mybatis 自动生成文件
[Java](http://lib.csdn.net/base/java "Java 知识库") -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
//查看进程堆栈
jmap -dump:live,format=b,file=heap.dmp 5699
jmap -dump:format=b,file=201709251725 5699
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) 它只能通过构造函数来创建:
StringBuffer sb1 = new StringBuffer(); //创建一个长度为16的StringBuffer对象,内容为空。
StringBuffer sb2 = new StringBuffer(10); //创建一个长度为10+16的StringBuffer对象,内容为空。
StringBuffer sb3 = new StringBuffer("abc"); //创建一个长度为3+16的StringBuffer对象。
(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/78403710
24.线程池参数问题
线程池的实现ThreadPoolExecutor
(1) corePoolSize
核心池的大小
(2) maxPoolSize
大于该值,表示已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常
(3) keepAliveTime
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit keepAliveTime的单位
(4) allowCoreThreadTimeout
是否允许核心线程空闲退出,默认值为false。
(5) workQueue
三种队列
如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放添加到queue中,而是直接抄家伙(thread)开始运行)
如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
(a) 直接提交 SynchronousQueue
(b) 无界队列 LinkedBlockingQueue
(c) 有界队列 ArrayBlockingQueue
参考:https://www.oschina.net/question/565065_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.死锁代码实现
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
public class MyTestSiSuo {
private static Object o1 = new Object();
private static Object o2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run(){
//抢占资源 o1
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 ---Get o1");
//需要资源o2 但是 t2 独占(未释放) -->互相竞争资源-->死锁
synchronized(o2){
System.out.println("t1 ---Get o2");
}
}
}
};
Thread t2 = new Thread(){
@Override
public void run(){
//抢占资源o2
synchronized (o2) {
System.out.println("t2 ---Get o2");
//需要资源 o1,但是 t1 独占(未释放) -->互相竞争资源-->死锁
synchronized (o1) {
System.out.println("t2 ---Get o1");
}
}
}
};
t1.start();
t2.start();
}
}
27.SpringMVC和Spring boot
(1) Spring MVC
提供了一种轻度耦合的方式来开发web应用。
Spring MVC是Spring的一个模块,式一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。
(2) Spring Boot
Spring Boot实现了自动配置,降低了项目搭建的复杂度。
众所周知Spring框架需要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的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条数据了,就产生了幻读。
参考:https://blog.csdn.net/sinat_33536912/article/details/51200630
https://blog.csdn.net/yang1982_0907/article/details/44408809
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()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
join()
使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
//在一个线程中调用other.join(),将等待other执行完后才继续本线程。
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/70214612
37.ibatis和Mybatis
(1) ibatis配置sqlMapConfig.xml
如:
参考:https://blog.csdn.net/geyouchao/article/details/51354571
https://blog.csdn.net/bruce128/article/details/71914755
38.拦截器和过滤器的区别
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
39.重定向(redirect)和转发(forward)
转发(forward)
(1) 是服务器内部的重定向,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的
(2) 关于request: 由于在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者servlet中使用。
redirect(重定向):
(1) 是客户端的重定向,是完全的跳转。即服务器返回的一个url给客户端浏览器,然后客户端浏览器会重新发送一次请求,到新的url里面,因此浏览器中显示的url网址会发生变化。
(2) 因为这种方式比forward多了一次网络请求,因此效率会低于forward。
40.java算法
参考:https://blog.csdn.net/l1028386804/article/details/51097928/
41.java设计模式
共23种
(1) 单例模式
(2) 工厂模式
(3) 生产者消费者模式
(4) 观察者模式
(5) 适配器模式
(6) 代理模式
参考:https://www.cnblogs.com/cr330326/p/5627658.html
https://blog.csdn.net/a394268045/article/details/51801258
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是进行预编译的。
public class PreparedStatementTest {
public static void main(String[] args) {
test_autoCommit();
}
public static void test_autoCommit()
{
String driver="oracle.jdbc.driver.OracleDriver";
String url="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String user="briup";
String password="briup";
Connection conn=null;
PreparedStatement ps=null;
try {
//1、注册驱动
Class.forName(driver);
//2、获取连接
conn= DriverManager.getConnection(url, user, password);
//System.out.println(conn);
//3、创建prepareStatement对象
String sql="insert into lover values(?,?,?)";
ps=conn.prepareStatement(sql);
//4、执行sql语句
ps.setInt(1,21);//代表设置给第一个?号位置的值为Int类型的21
ps.setString(2,"suwu150");//代表设置给第二个?号位置的值为String类型的suwu150
java.util.Date utilDate=new java.util.Date();//进行类型转换,由util类型的date转化为sql类型的
ps.setDate(3, new java.sql.Date(utilDate.getTime()));
//ps.execute();//执行
System.out.println(ps.execute());//执行表输出返回的结果,结果为false,因为没有返回的结果集
//5、处理结果集
} catch (Exception e) {
e.printStackTrace();
}
finally{
//6、关闭资源
try {
if(ps!=null)ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
(f)connection设置为readOnly,Connection是对书库连接,属于重量级,我们使用即可。
连接池的使用,我们可以修改数据库默认的连接数。
参考:http://www.jb51.net/article/102831.htm
43.多线程同步
(1)同步方法:synchronized关键字修饰的方法
public synchronized void save(){}
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
(2)同步代码块 :synchronized关键字修饰的语句块
synchronized(object){
}
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
(3)用特殊域变量(volatile)实现线程同步
(a) volatile关键字为域变量的访问提供了一种免锁机制,
(b) 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
(c) 因此每次使用该域就要重新计算,而不是使用寄存器中的值
(d) volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
(4)使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
class Bank {
private int account = 100;
//需要声明这个锁
private Lock lock = new ReentrantLock();
public int getAccount() {
return account;
}
//这里不再需要synchronized
public void save(int money) {
lock.lock();
try{
account += money;
}finally{
lock.unlock();
}
}
}
注:关于Lock对象和synchronized关键字的选择:
(a) 最好两个都不用,使用一种java.util.concurrent包提供的机制,
能够帮助用户处理所有与锁相关的代码。
(b) 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
(c) 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
(5)使用局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响
public class Bank{
//使用ThreadLocal类管理共享变量account
private static ThreadLocal account = new ThreadLocal(){
@Override
protected Integer initialValue(){
return 100;
}
};
public void save(int money){
account.set(account.get()+money);
}
public int getAccount(){
return account.get();
}
}
注: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);
1.选择唯一性索引
唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。
2.为经常需要排序、分组和联合操作的字段建立索引
经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。
3.为常作为查询条件的字段建立索引
如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。
4.限制索引的数目
索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。
5.尽量使用数据量少的索引
如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。
6.尽量使用前缀来索引
如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。
7.删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
8 . 最左前缀匹配原则,非常重要的原则。
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a 1=”” and=”” b=”2” c=”“> 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
9 .=和in可以乱序。
比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
10 . 尽量选择区分度高的列作为索引。
区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就 是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条 记录
11 .索引列不能参与计算,保持列“干净”。
比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本 太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
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底层数据结构是红黑树。(是一种自平衡的二叉树)如何保证元素唯一性呢?
根据比较的返回值是否是0来决定如何保证元素的排序呢?两种方式自然排序(元素具备比较性)让元素所属的类实现Comparable接口比较器排序(集合具备比较性)让集合接收一个Comparator的实现类对象Map(双列集合)A:Map集合的数据结构仅仅针对键有效,与值无关。B:存储的是键值对形式的元素,键唯一,值可重复。
HashMap底层数据结构是哈希表。线程不安全,效率高哈希表依赖两个方法:
hashCode()和equals()执行顺序:首先判断hashCode()值是否相同是:继续执行equals(),看其返回值是true:说明元素重复,不添加是false:就直接添加到集合否:就直接添加到集合最终:自动生成hashCode()和equals()即可LinkedHashMap底层数据结构由链表和哈希表组成。由链表保证元素有序。由哈希表保证元素唯一。
Hashtable底层数据结构是哈希表。线程安全,效率低哈希表依赖两个方法:
hashCode()和equals()执行顺序:首先判断hashCode()值是否相同是:继续执行equals(),看其返回值是true:说明元素重复,不添加是false:就直接添加到集合否:就直接添加到集合最终:自动生成hashCode()和equals()即可TreeMap底层数据结构是红黑树。(是一种自平衡的二叉树)如何保证元素唯一性呢?根据比较的返回值是否是0来决定如何保证元素的排序呢?两种方式自然排序(元素具备比较性)让元素所属的类实现Comparable接口比较器排序(集合具备比较性)让集合接收一个Comparator的实现类对象
2:到底使用那种集合(自己补齐)看需求。
是否是键值对象形式:是:Map键是否需要排序:是:TreeMap否:HashMap不知道,就使用HashMap。否:Collection元素是否唯一:是:Set元素是否需要排序:是:TreeSet否:HashSet不知道,就使用HashSet否: List要安全吗:是:Vector(其实我们也不用它,后面我们讲解了多线程以后,我在给你回顾用谁) 否:ArrayList或者LinkedList增删多:LinkedList查询多:ArrayList不知道,就使用ArrayList不知道,就使用ArrayList3:集合的常见方法及遍历方式Collection:add()remove()contains()iterator()size()遍历:增强for迭代器|--Listget()遍历:普通for|--SetMap:put()remove()containskey(),containsValue()keySet()get()value()entrySet()size()遍历:根据键找值根据键值对对象分别找键和值作业:我讲解过的任意一个集合,我要求你存储什么,你就能够存储什么。并且,还要能够遍历出来。
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://blog.csdn.net/qq_22654611/article/details/52606960/