java面试volatile关键字,cas自旋,锁,集合

volatile 关键字

	1.保证了内存的可见性:
			所有线程共享的变量都存储在主内存中,每个线程有一个自己的工作内存,当操作主内存变量是会先拿到主内存中的数据到自己的工作内存,操作完之后再将数据同步到主内存中,这就会造成A线程在给数据进行++的时候,还未同步到主内存的时候,B线程也对数据进行了++,这样讲数据同步回去的时候,实现上AB线程都做了++,但是数据的值只+1,使用volatile关键字的作用,就是A线程在操作数据的时候,一旦把值进行了改变,就会通知到主内存,那么B线程此时就得重新去主内存中拿值
	2.防止指令重排:
			指令的执行顺序并不一定会像我们编写的顺序那样执行,volatile有序性是通过内存屏障实现的。JVM和CPU都会对指令做重排优化,所以在指令间插入一个屏障点,就告诉JVM和CPU,不能进行重排优化。
	3.不保证原子性
			尽管volatile关键字可以保证内存可见性和有序性,但不能保证原子性。举个列子:1000个线程同时对共享数据num进行++,线程1对num进行了++,线程2在线程1还未进行num++的时候拿到了num,但是现在发现了num值发生了改变,那么线程2就不会再对num进行++,而实重新去内存获取num,这样就可能造成了线程2没操作,num少加了一次

CAS自旋

	也是乐观锁,需要操作3个数据:地址值V,预期值A,更新后的值B,当且就当V与A相等的时候,才会把值更新为B,利用的就是无限循环和comparaAndSet(A,B)方法,当comparaAndSet方法为true的时候,返回true结束循环。
	缺点:		1.ABA问题,当数据从A变成B在变成A,cas自旋会认为数据并未发生改变,不会做出操作,解决的思路就是加上版本号1A-》2B-》3A
				2.长时间失败,会导致一直处于循环状态,占用cpu,影响性能
				3.只能保证一个共享变量的原子性,多个共享变量的原子性无法保证。(Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。)

	1.悲观锁与乐观锁
		悲观锁:悲观锁认为自己在操作数据的时候,一定会有其他线程来操作数据,因此在获取数据的时候,就会加上锁,防止其他线程操作,synchrozied和lock都是悲观锁
		乐观锁:乐观锁认为自己在操作数据的时候,不会有其他线程来操作,因为不会加锁,只会在更新数据的时候,判断一下该数据有没有被其他线程修改过,如果有就放弃操作,报出异常或者自旋,cas自旋就是乐观锁常用的策略
	2.无锁,偏向锁,轻量锁,重量锁(锁的信息会存在对象头)
		1.无锁:不对资源进行加锁,所有线程都可以访问和操作,它的特点就是会在修改数据的时候进行循环操作,总会有一个线程修改成功,其他线程则一直循环,直到修改成功。也会是CAS自旋原理
		2.偏向锁:数据总是由一个线程操作,没有其他线程竞争,此时就会形成偏向锁,它的特点是mark word会存入线程的id
		3.轻量锁:处于偏向锁状态时,发生锁竞争,此时就会进入轻量锁状态。它的特点就是,不会阻塞线程,线程会以自旋的形式获取锁
		4重量锁:当线程越来越多,或者一个线程的自旋超过一定的次数,此时就会变成重量锁,为获取到锁的线程会进入阻塞状态
	3.可重入锁与不可重入锁
		可重入锁:一个线程在调用外层方法的时候,进入该线程的内层方法也是可以的(前提是同一个锁对象),一定程度上避免了死锁,lock和synchrozied都是可重入锁
		不可重入锁:遇上相反,会造成死锁。
	4.共享锁与独享锁
		独享锁:也称排他锁,一个线程对数据A上锁后,不允许其他线程对数据A在加上如何形式的锁,获得锁的线程,可读数据,也可以写数据,synchrozied和lock都是独享锁
		共享锁:一个线程多数据A加锁后,允许其他线程再给数据A加上,但是只能读数据,不能写数据。

集合

所有集合的根接口是collection接口,实现他的4大子接口的分别是:List,Set,Queue,Map
1.List接口
	list接口的主要实现类是,ArrayList,LinkedList,Vector,都是有序可重复集合,存入什么样取出也是什么样,数据可重复
	1.1.ArrayList底层实现是Object动态扩容数组,每次扩容的大小为原来的0.5倍,非线程安全集合,查询速度快,增删速度慢
	1.2.LinkedList底层实现是双向链表,非线程安全集合,增删速度快,查询速度慢
	1.3.Vector底层实现是动态扩容Object数据,每次扩容大小为原来的1倍,是线程安全的集合,线程安全那也就意味着效率更低
2.Set接口
	set接口的主要实现类是,hashSet,treeSet,都是无序,不可重复集合
	2.1.hashSet底层是哈希表,哈希表存的是哈希值,存入元素会根据其哈希值存到相应的位子,如果哈希值i相同则会调用equels方法进行比较,如果还是相同,则判断是同一个元素,加入失败
	2.2.TreeSet底层是红黑树,红黑树左小右大,红黑树是一个带排序的集合,排序分为自然排序和自定义排序,自然排序:需要元素实现comparable接口,重写comparaTo方法。自定义排序:需要定义一个类实现comparator接口,重写compara方法,讲其对象传入treeSet构造方法中,当同时具备comparable,和comparator时,以comparator为主。
3.map
	3.1.hashMap底层实现由Entry数组和链表组成,数组是主体,链表时为了解决hash冲突的,通过计算存储元素key的hash值,找到对应的数组下标存储,hashMap默认大小时16,默认负载因子时0.75,当存储元素大于(16*0.75)时,会扩展为原来的2倍,当链表的长度超过阈值(默认是8),会将链表转成红黑树,当低于6的时候,会从红黑树转成链表,hashMap的put方法菜是真正创建数组的时候,get方法会根据给的key计算出hash值找到对用的位置,如果此时对应的链表不为空,会调用equls方法比较key。
	3.2.hashTable底层也是数组和链表组成。不同的是hashTable默认大小是11,发生扩展时会变成原来的2n+1,不允许出现任何键值为null,hashMap允许存在一个键为null,多个值为null,hashTable是线程安全的,而hashMap非线程安全,效率更高
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值