Java中的锁

 

一、自旋锁与互斥锁
 
java中的锁整体分为两大类,基于synchronized关键字的互斥锁和基于CAS操作的自旋锁
synchronized:串行执行阻塞的、用户态内存态切换的重量级锁。(监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的)
CAS:非阻塞的自旋、线程上下文切换
 
异同点:
  • 自旋锁与互斥锁都是为了实现保护资源共享的机制。
  • 无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
  • 获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;
  • 获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。
 
 
二、悲观锁与乐观锁
 
乐观锁
Java中CAS操作,就是一种乐观锁,前文也讲过了详细的知识点。读数据,不上锁,乐观认为别的线程不会修改。 写数据,判断下其他线程有没有更新数据,先读取数据,比较数据,写入数据,失败重复操作。
 
悲观锁
Java中Synchronized,就是一种悲观锁。是一种悲观思想,总认为别人会来修改数据,所以每次读写数据,都会上锁。
 
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。
 
 
三、公平锁和非公平锁
 
并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认非公平锁
 
区别:
公平锁:在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自已。
 
非公平锁:非公平锁比较粗鲁,上来就直接占有锁,如果尝试失败,就再采用类似公平锁那种方式。ReentrantLock底层实现是CAS+队列实现的,即非公平锁也是基于队列基础之上的,非公平锁就是线程一上来就先去抢,没抢到再加入到那个队列中。
 
 
四、可重入锁与非可重入锁
 
可重入锁又名递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
synchronize 和ReentrantLock 就是一个典型的可重入锁,可重入锁最大的作用就是避免死锁。
 
 
五、独享锁与共享锁(读/写锁)
 
独占锁:指该锁一次只能被一个线程持有。对于ReentrantLock、synchronized而言都是独占锁
共享锁:该锁可以被多个线程持有。
ReentrantReadWriteLock 共读锁为共享锁,写锁为独占锁。主要实现读共享,写互斥功能,对比单纯的互斥锁在共享资源使用场景为频繁读取及少量修改的情况下可以较好的提高性能。
 
 
锁的四种状态
  1. 无锁
  2. 偏向锁
  3. 轻量级锁
  4. 重量级锁
 
 

 

posted @ 2020-04-30 18:30  cao_xiaobo  阅读(173)  评论(0编辑  收藏  举报