简介
在并发中,有时对同一个对象操作时或保证线程同步的情况下我们需要使用到锁,再如一些并发场景中同时操作一个非并发安全的对象(HashMap)这个时候也需要使用到锁。简单来说,锁是一种用于控制对共享资源访问的一种工具
分类
Java中的锁住要有一下几种
- 乐观锁、悲观锁
- 可重入锁、非可重入锁
- 公平所、非公平所
- 共享锁、排它锁
- 自旋锁、阻塞锁
- 可中断锁
获取锁的方式
锁一般(synchronized是JVM实现的锁,这里说的是非此类的锁)都实现了Lock接口,而获取锁的有四种方式
lock()
- 最普通的获取锁的方式,当一个线程拿到锁后,其它线程需要等待
- 因为锁需要自己释放,如果在拿到锁的运行期间没有释放锁,那么其他线程将会永远无法获取该锁,所以一般都是在finally块中释放锁
- lock方法不能被中断,会来带一些安全问题(死锁)
tryLock()
- 从方法名字中就可以明白,他是尝试获取锁。如果当前锁没有被其它线程占用则获取锁成功返回true,否则返回false代表获取锁失败。
- 该方法不用等待会立即返回结果
- 与lock相比更灵活一些,我们可以根据是否获取到锁了然后进行后续的程序处理。
tryLock(long time, TimeUnit unit)
- 该方法也是尝试获取锁,与tryLock()相比它不是立即返回结果
- 在time时间内如果获取到锁会放回true,否则等待time时间并且没有获取锁返回false
lockInterruptibly()
- 与tryLock(long time, TimeUnit unit) 相比lockInterruptibly()等待的时间设置为无限
分类
悲观锁
对数据进行修改时,先把数据给锁上,然后再对数据进行修改。
缺点
这种锁的争抢是通过线程的阻塞和唤醒实现的,所以性能上有缺点。性能的问题主要发生的线程的上下文切换、操作系统的用户态和核心态切换或者检查是否有线程需要被唤醒等方面上。
乐观锁
先对数据进行修改,然后对数据进行更新,更新的时候会检查在自己对数据操作的过程中是否有其他线程对数据做了修改的动作,简单来说是用CAS算法实现的
缺点
如果程序上锁的时间很长或者不停的重试,那么开销的资源会很大