赞
踩
@BeforeClass > @Before >@Test>@After> @AfterClass
Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境如果我们需要加锁就显得无能为力。在单个JVM实例上,锁的竞争者通常是一些不同的线程,而在分布式环境中,锁的竞争者通常是一些不同的线程或者进程。如何实现在分布式环境中对一个对象进行加锁呢?答案就是分布式锁。
目前分布式锁的实现方案主要包括三种:
nginx的负载均衡调度算法方式如下:
轮询(默认算法):每个请求按时间顺序分配到不同后端服务器,如果某个后端服务器宕机,能自动剔除掉。
weight轮询:nginx反向代理接受到客户端收到的请求后,可以给不同的后端服务器设置一个权重值(weight),用于调整不同服务器上请求的分配率,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器配置进行配置的。比如说有些服务器的硬件配置高,比重就会比较大一点。
ip_hash:每个请求按照发起客户端ip的hash结果进行匹配,这样的算法每一个固定的ip地址的客户端总会访问到同一个后端服务器,这也在一定程度上解决了集群部署环境下session共享的问题。
fair:智能调整调度算法,动态的根据后端服务器的请求处理器的请求处理响应的时间来进行均衡分配,响应时间短,处理效率高的服务器分配到请求的概率高,响应时间长,处理效率低的服务器分配到的请求少;结合了前两者的优点的一种调度算法。但是需要注意的是nginx默认不支持fair算法,如果要使用这种算法,需要安装upstream_fair模块。
url_hash:按照访问的url的hash结果分配请求,每个请求的url会指向后端固定的某个服务器,可以在nginx作为静态服务器的情况下提高缓存效率。同样要注意Nginx默认不支持这种调度算法,要使用的话需要安装nginx的hash软件包。
1、join
join —— 让一个线程等待另一个线程完成才继续执行。如A线程执行体中调用B线程的join()方法,则A线程被阻塞,直到B线程执行完为止,A才能得以继续执行
2、sleep
sleep —— 让当前的正在执行的线程暂停指定的时间,并进入阻塞状态。在其睡眠的时间段内,该线程由于不是处于就绪状态,因此不会得到执行的机会。即使此时系统中没有任何其他可执行的线程,出于sleep()中的线程也不会执行。因此sleep()方法常用来暂停线程执行。
前面有讲到,当调用了新建的线程的start()方法后,线程进入到就绪状态,可能会在接下来的某个时间获取CPU时间片得以执行,如果希望这个新线程必然性的立即执行,直接调用原来线程的sleep(1)即可。
3、yield
当某个线程调用yiled()方法从运行状态转换到就绪状态后,CPU从就绪状态线程队列中只会选择与该线程优先级相同或优先级更高的线程去执行。
4、改变线程的优先级
每个线程在执行时都具有一定的优先级,优先级高的线程具有较多的执行机会。每个线程默认的优先级都与创建它的线程的优先级相同。main线程默认具有普通优先级。
设置线程优先级:setPriority(int priorityLevel)。参数priorityLevel范围在1-10之间,常用的有如下三个静态常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
获取线程优先级:getPriority()。
注:具有较高线程优先级的线程对象仅表示此线程具有较多的执行机会,而非优先执行。
5、将线程设置成守护线程(jvm中的垃圾回收线程)----后台线程
概念:后台线程主要是为其他线程(相对可以称之为前台线程)提供服务,或“守护线程”。如JVM中的垃圾回收线程。
生命周期:后台线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡
设置后台线程:调用Thread对象的setDaemon(true)方法可以将指定的线程设置为后台线程。
sleep和wait的区别在于这两个方法来自不同的类分别是Thread和Object,sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep和wait的区别: sleep是线程被调用时,占着cpu去睡觉,其他线程不能占用cpu,os认为该线程正在工作,不会让出系统资源,wait是进入等待池等待,让出系统资源,其他线程可以占用cpu,一般wait不会加时间限制。
HashMap的底层实现原理是面试中出现频率非常高的一道面试题,本文将对HashMap的底层实现原理做一个简要的概况和总结,便于复习。
一、对于Map集合存储结构的理解
首先介绍以HashMap为典型代表的Map集合的存储结构
① Map中的key:无序的、不可重复的,底层使用Set集合存储key;key所在的类要重写equals()和hashCode() 。
② Map中的value:无序的、可重复的,底层使用Collection集合存储value;value所在的类要重写equals() 。
③ Map中的entry:无序的、不可重复的,底层使用Set集合存储entry,每个entry都相当于一组
key-value 。
二、HashMap的常用方法
如果想要使用HashMap,我们需要对HashMap的常用方法有一个基本的了解:
* 添加:put(Object key,Object value)
* 删除:remove(Object key)
* 修改:put(Object key,Object value)
* 查询:get(Object key)
* 长度:size()
* 遍历:keySet() / values() / entrySet()
三、HashMap的内存结构说明
① HashMap在jdk7中的实现原理
HashMap对象在实例化后,底层创建了一个长度为16的一维数组 Entry[ ] table
当执行方法 put(key1,value1) 添加数据时,HashMap底层的实现原理如下(此前可能已经执行过多次put()方法):
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过底层算法的计算以后,可以得到在Entry数组中的存放位置,接下来又分为三种情况。
情况1:如果此位置上的数据为空,此时的key1-value1添加成功。
情况2:如果此位置上数据不为空(意味着此位置上存在一个或多个数据(多个数据以链表的形式存在)),则比较key1和已经存在的数据的哈希值,如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功,即key1-value1和原来的数据会以链表的形式存储。
情况3:与情况二类似,在比较key1和已经存在的数据的哈希值时,如果key1的哈希值和某个已经存在的数据(如key2-value2)的哈希值相同,则调用key1所在类的equals()方法,返回key1和key2的比较结果,如果返回的是false,此时key1-value1添加成功,同样也是和原来的数据以链表的形式存储;如果返回的是true,则会使用value1替换value2。
在不断添加数据的过程中,会涉及到数组的扩容问题。当添加数据超出临界值(且数据要存放的位置非空)时,需要对数组扩容。默认的扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
② HashMap在jdk8中相较于jdk7在底层实现方面的不同
(1). 使用空参构造器创建HashMap对象时,底层没有直接创建一个长度为16的数组。
(2). jdk8底层存储数据的数组使用的是Node[ ],而不是Entry[ ] 。
(3). jdk8在首次调用put()方法时,底层才会创建长度为16的数组。
(4). jdk7中底层存储数据的结构是:数组+链表。jdk8中底层存储数据的结构是:数组+链表+红黑树。
(5).当存储数据形成链表时,链表的结构不相同,jdk7的链表结构是:新的数据指向旧的数据。jdk8的链表结构是:旧的数据指向新的数据。
(6).当底层数组的某一个索引位置上的元素以链表形式存在的数据个数大于8 ,且当前数组的长度大于64时,此时这个索引位置上的数据改为使用红黑树存储。
四、HashMap底层重要属性的说明
DEFAULT_INITIAL_CAPACITY:HashMap的默认容量:16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
threshold:扩容的临界值 = 容量 * 填充因子:16 * 0.75 = 12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:Bucket中Node被转化为红黑树时最小的Hash表容量:64
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。