Java面试--锁

Java锁机制

1.Java中锁是什么?起到什么作用?

是用于控制多个线程对共享资源的并发访问的一种同步机制。它确保了同一时间只有一个线程能够执行某段代码,从而避免了多线程同时操作,导致数据不一致的问题。Java中包含多种锁用来处理各种复杂的并发情况。

2.什么是乐观锁和悲观锁?

乐观锁:
① 就是执行操作时觉得不会出现任何问题,在提交的时候对对应的数据进行验证。
② 更适合读操作多的场景,因为不加锁会让读操作的性能提升。
悲观锁:
①认为执行任何操作都可能会出现问题,每一个都上了锁,执行完才会释放锁,会造成线程阻塞和死锁的情况。
② 更适合写操作多的场景,因为先加锁可以保证数据的正确。

3.怎么实现乐观锁?

①版本号机制:在数据表加上一个版本号字段,每次提交会更改此字段值加一,操作的时候也会读取此字段,会拿读取到的字段和当前字段值对比,相同才会操作。
②CAS:也是比较会有三个参数:V:需要更改的变量值。E:预期值。N:新值。拿V和E进行比较相同才会执行。

4.CAS会出现的问题?

  • ABA:出现ABA问题,如果A先去操作了数据成功后B也去操作并改回了原来的值,C读取的数据和更改后数据一样这样也会被执行。但是这个数据已经被修改过一次了。
    解决思路:再增加一个唯一标识,版本号或者时间戳。
  • 循环开销时间大:cas通过自旋自己来重试操作,一直循环直到成功,长时间不成功会给cpu很大开销
    只能保证一个共享变量的原子操作:cas只对单个共享变量有效涉及多个便无效。

5.自旋锁和自适应自旋锁?

  • 因为线程竞争,会导致线程阻塞或者挂起,但是如果同步资源的锁定时间很短,那么阻塞和挂起的花费的资源就得不偿失。
  • 自旋锁: 当竞争的同步资源锁定时间短,就让线程自旋,如果自旋完成后,资源释放了锁,那线程就不用阻塞,直接获取资源,减少了切换线程的开销。实现原理是CAS,前面提过cas就是自旋自己来重试操作。
  • 缺点:占用了处理器的时间,如果锁被占用的时间短还好,如果久就会浪费了处理器的时间。所以要限定自旋次数(默认是10次,可以使用-XX:PreBlockSpin来更改)没有成功获得锁,就应当挂起线程。
  • 自适应自旋锁: 自旋次数不固定,是由上一个在同一个锁上的自旋时间和锁拥有者的状态决定。如果在同一个锁对象上,自旋刚刚获得锁,并且持有锁的线程在运行,那么虚拟机会认为这次自旋也可能成功,那么自旋的时间就会比较长,如果某个锁,自旋没成功获得过,那么可能就会直接省掉自旋,进入阻塞,避免浪费处理器时间。

6.sychronized的无锁,偏向锁,轻量级锁,重量级锁?

这四个锁是专门针对sychronized的,实现引入了大量的优化,并且synchronized有多种锁状态。级别从低到高依次是:无锁、偏向锁、轻量级锁和重量级锁,锁状态只能升级不能降级。

  • 无锁: 就是乐观锁。
  • 偏向锁: 当只有一个线程访问加锁的资源,不存在多线程竞争的情况下,那么线程不需要重复获取锁,这时候就会给线程加一个偏向锁。(避免CAS)
  • 轻量级锁: 是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。(CAS+自旋)
  • 重量级锁: 若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。(将除了拥有锁的线程以外的线程都阻塞)。

7.ReentrantLock公平锁和非公平锁?

  • 公平锁: 多个线程按照申请锁的顺序来获取锁。 Lock lock = new ReentrantLock(true); 默认是非公平锁,设置为true是公平锁。
    优点:等待线程不会饿死。
    缺点:CPU唤醒线程的开销比非公平锁要大。
  • 非公平锁: 多个线程获取锁的顺序并不是按照申请锁的顺序。 sybchronized和lock都是非公平锁。
    优点:减少唤醒线程得开销。
    缺点:可能会出现线程饿死或者很久获得不了锁。

8.可重入锁?

可重入锁: 也叫递归锁,同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁。(前提:锁对象是同一个对象或者类)。ReentrantLock和synchronized都是可重入锁。

9.sychronized和ReentrantLock区别?

  1. sychronized是关键字,ReentrantLock是一个类。
  2. sychronized会自动加锁和释放锁,ReentrantLock需要手动加锁和释放锁
  3. sychronized是jvm层面的锁,ReentrantLock是api层面的锁实现lock接口
  4. sychronized是非公平锁,reentrantLock是可选择公平锁和非公平锁。
  5. sychronized锁的是对象,锁信息在对象头中,ReentrantLock通过int类型的state来表示锁的状态
  6. sychronized底层有锁升级过程。

10.独享锁和共享锁?

  • 独享锁: 独占锁是指锁一次只能被一个线程所持有。如果一个线程对数据加上排他锁后,那么其他线程不能再对该数据加任何类型的锁。获得独占锁的线程即能读数据又能修改数据。synchronized和Lock的实现类就是独占锁。
  • 共享锁: 共享锁是指锁可被多个线程所持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据。

11.互斥锁和读写锁?

  • 互斥锁: 是独享锁的实现,某一资源同时只允许一个访问者对其访问。具有唯一和排它性。
  • 读写锁: 是共享锁的实现,读写锁管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,而写锁是独占的。写锁的优先级要高于读锁。并发度要比互斥锁高,因为可以拥有多个读锁。

12.分段锁?

是锁的设计,不是具体的某一种锁,分段锁的设计目的就是细化锁的粒度,当操作不需要更新整个数据的时候,就仅仅针对数据中的一项进行加锁操作。ConcurrentHashMap的锁机制在jdk1.7用的就是分段锁。

13.注意事项

  • 避免死锁:在使用锁时,要注意避免死锁的发生。死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法向前推进。
  • 锁的粒度:锁的粒度越细,并发性能通常越好,但也会增加同步的开销。因此,在设计并发程序时,需要根据实际情况权衡锁的粒度。
  • 锁的顺序:在多个线程需要获取多个锁时,要确保它们以相同的顺序获取锁,以避免发生死锁。
  • 避免长时间持有锁:尽量缩短锁的持有时间,以减少其他线程等待锁的时间。可以使用try-finally语句块来确保锁的及时释放。
  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值