java 中锁的概念
简单的理解一下
乐观锁(也是自旋锁)
为了不放弃 CPU 执行事件, 循环的使用 cas 技术 (在更改值时先再次获取值看值是否与刚才获取的相同, 不相同说明被其他线程改变, 则不进行操作, 进行 while 循环, 直到相同为止, 再对值进行操作) 对数据尝试进行更新, 直到成功.
悲观锁
假定会发生并发冲突, 同步所有对数据的相关操作, 从读数据就开始上锁.
乐观锁
假定没有冲突, 在修改数据时, 如果发现数据和之前获取的不一样, 则读最新数据, 重试后修改
公平锁
争抢锁的顺序, 如果按先来后到, 则为公平
不公平锁
与公平锁相反
独享锁(写)
给资源加上写锁, 线程可以修改资源, 其他线程不能再加锁;(单写)
共享锁(读)
给资源加上读锁后只能读不能改, 其他线程也只能加读锁, 不能加写锁;(多读)
可重入锁
线程拿到一把锁之后, 可以自由进入同一把锁所同步的其他代码, 比如一个方法中的 synchronized 块中再次调用该方法, 再次进入 synchronized 块, lock 也是如此, 同一个线程 lock 获取锁后, 可以再次获取, 并且 lock 提供有获取锁的次数的方法, 同时释放时, 也要释放相应的次数, 多次调用 unlock
不可重入锁
与可重入锁相反
开始详细简绍
1. 乐观锁
顾名思义, 就是很乐观, 每次去拿数据的时候都认为别人不会修改, 所以不会上锁, 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据, 可以使用版本号等机制.
乐观锁适用于多读的应用类型, 乐观锁在 Java 中是通过使用无锁编程来实现, 最常采用的是 CAS 算法, Java 原子类中的递增操作就通过 CAS 自旋实现的.
CAS 全称 Compare And Swap(比较与交换), 是一种无锁算法. 在不使用锁 (没有线程被阻塞) 的情况下实现多线程之间的变量同步. java.util.concurrent 包中的原子类就是通过 CAS 来实现了乐观锁.
2. 悲观锁
总是假设最坏的情况, 每次去拿数据的时候都认为别人会修改, 所以每次在拿数据的时候都会上锁, 这样别人想拿这个数据就会阻塞直到它拿到锁.
典型的悲观锁就是 Java 的同步 synchronized 关键字.
3. 公平锁
就是很公平, 在并发环境中, 每个线程在获取锁时会先查看此锁维护的等待队列, 如果为空, 或者当前线程是等待队列的第一个, 就占有锁, 否则就会加入到等待队列中, 以后会按照 FIFO 的规则从队列中取到自己.
公平锁的优点是等待锁的线程不会饿死. 缺点是整体吞吐效率相对非公平锁要低, 等待队列中除第一个线程以外的所有线程都会阻塞, CPU 唤醒阻塞线程的开销比非公平锁大.
代码:
- public class LockDemo {
- public static void main (string[] args) throws In terruptedException {
- ReentrantLock lock=new ReentrantLock(true) ;
- for.(inti=0;i<10;i++){
- new Thread(new Runnable() {
- @Override
- public void run() {
- lock . lock() ;
- System. out . println("我是第"+ Thread.currentThread().getName()+"个线程") ;
- lock.unlock() ;
- }).start();
- }
- }
- }
结果:
4. 非公平锁
上来就直接尝试占有锁, 如果尝试失败, 就再采用类似公平锁那种方式.
非公平锁的优点是可以减少唤起线程的开销, 整体的吞吐效率高, 因为线程有几率不阻塞直接获得锁, CPU 不必唤醒所有线程. 缺点是处于等待队列中的线程可能会饿死, 或者等很久才会获得锁.
代码:
- public class LockDemo {
- public static void main(String[] args) throws InterruptedException {
- Reen trantLock lock = new Reen trantLock(false) ;
- for ( int i=0;i<10;i++){
- new Thread(new Runnable() {
- @Override
- public void run() {
- lock.lock();
System.out.printIn("我是第" + Thread. currentThread() . getName() +" 个线程);
- lock.unlock();
- }).start();
- }
- }
- }
运行结果:
5. 独享锁和共享锁
独享锁和共享锁在你去读 C.U.T 包下的 ReeReentrantLock 和 ReentrantReadWriteLock 你就会发现, 它俩一个是独享一个是共享锁.
独享锁: 也叫互斥锁, 是指该锁一次只能被一个线程所持有. 常见的有 ReentrantLock,ReadWriteLock.
共享锁: 是指该锁可被多个线程所持有. 常见的有 CountDownLatch.
6. 可重入锁
广义上的可重入锁指的是可重复可递归调用的锁, 在外层使用锁之后, 在内层仍然可以使用, 并且不发生死锁(前提得是同一个对象或者 class), 这样的锁就叫做可重入锁. ReentrantLock 和 synchronized 都是可重入锁.
上面的代码就是一个可重入锁的一个特点, 如果不是可重入锁的话, setB 可能不会被当前线程执行, 可能造成死锁.
7. 不可重入锁
不可重入锁, 与可重入锁相反, 不可递归调用, 递归调用就发生死锁. 看到一个经典的讲解, 使用自旋锁来模拟一个不可重入锁.
来源: http://www.bubuko.com/infodetail-3477968.html