Java中各种锁的介绍了解了锁的特性后,在平时的开发中就可以灵活运用了。
1.乐观锁和悲观锁
悲观锁永远假设最坏的情况,你每次去拿数据的时候,你认为别人会修改它,所以每次拿数据的时候,你都会加锁,这样,其他想要获取此数据的人将阻塞,直到获得锁定为止。Java中的synchronized、ReentrantLock等独占锁都是悲观锁思想的实现。
乐观锁认为在使用数据时不会有其他线程修改数据,所以不会加锁。它只是在更新数据时判断其他线程是否更新了数据。如果没有更新过,写自己的数据,否则不写。悲观锁主要是通过CAS算法在Java中实现的。
悲观锁适合写操作多的场景,乐观锁适合读操作多的场景。
2.公平锁和非公平锁
公平锁是指多个线程按照申请锁的先后顺序获取锁。
不公平锁是指多个线程获取锁的顺序与申请锁的顺序不一致。有可能后申请的线程比先申请的线程先获取锁。
非公平锁具有更好的性能和吞吐量,但可能会导致饥饿。
3.可重入锁和不可重入锁
可重入锁指的是可以重复递归调用的锁。在外层使用锁后,内层仍然可以使用,并且不会发生死锁(前提是同一个对象或类),这样的锁称为可重入锁。ReentrantLock和synchronized都是可重入锁。
不可重入锁与可重入锁相反,不能递归调用,递归调用会出现死锁。
publicsynchronizedvoidgetA(){ 得到B(); } 公共同步无效getB(){ }
上面的代码中,getA中调用了getB方法,可以直接进入,不会死锁。
4.独占锁和共享锁
独占锁:这种锁一次只能被一个线程持有。
共享锁:这个锁可以被多个线程共享,典型的就是ReentrantReadWriteLock中的读锁。它的读锁可以共享,但是它的写锁每次只能独占。
公开课LockTest{ privatestaticReentrantReadWriteLockreadWriteLock=newReentrantReadWriteLock(); publicstaticvoidmain(String[]args){ ExecutorServiceexecutorService=执行者。新固定线程池(2); 执行者服务。执行(()->{ 重入读写锁。ReadLockreadLock=readWriteLock。读锁(); 读锁。锁(); System.out.println("锁定线程1"+Thread.class.getName()); }); 执行者服务。执行(()->{ 重入读写锁。ReadLockreadLock=readWriteLock。读锁(); 读锁。锁(); System.out.println("锁定线程2"+Thread.class.getName()); }); System.out.println("结束"); } }
如果是独占锁,锁线程只会打印一次,共享锁会打印
5。偏向锁,轻量级锁和重量级锁
偏向锁指的是一段同步代码已经被一个线程访问过,线程自动获取锁。降低获取锁的成本。
轻量级锁是指当锁是偏向锁,被其他线程访问时,偏向锁会升级为轻量级锁,其他线程会尝试通过自旋来获取锁。不阻塞,提高性能。
重量级锁的意思是当锁为轻量级锁时,虽然有另一个线程在自旋,但不会一直自旋下去。当它旋转一定次数后,它还没有被获取。锁会进入阻塞,锁会膨胀成重量级锁。重量级锁会阻塞其他应用的线程并降低性能。
偏向锁加锁和解锁不需要额外的消耗,与执行异步方法相比只有纳秒级的差距。如果线程间存在锁竞争,会带来锁取消的额外消耗。适用于只有一个线程访问synchronized块的场景。
轻量级锁竞争线程不会被阻塞,提高了程序的响应速度。如果从不竞争锁的线程使用自旋,就会消耗CPU。适合追求响应时间的。同步块执行速度非常快
重量级锁线程竞争不使用自旋,不消耗CPU。线程阻塞,响应时间慢。适合追求吞吐量。
关注公众号:蜜蜂科技巢了解更多知识