锁的总结

Posted juncaoit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了锁的总结相关的知识,希望对你有一定的参考价值。

1.大纲

  Lock接口

  锁的分类

  乐观锁和悲观锁

  可重入锁与非可重入锁

  公平锁与非公平锁

  共享锁与排它锁

  自旋锁与阻塞锁

  可中断锁

  锁优化

 

一:Lock接口

1.锁

  是一种工具,用于控制对共享资源的访问

  Lock和synchronized,是常见的锁,都可以达到线程安全的目的

  Lock最常见的实现类是ReenTrantLock

  

2.为啥用Lock

  synchronized不够用

    效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在尝试获得锁的线程

    不够灵活:加锁与释放锁单一,每个锁仅有单一的条件

    无法知道是否成功获取锁

 

3.Lock的主要方法

  lock()

  tryLock()

  tryLock(long time, TimeUnit unit)

  lockInterruptibly()

 

4.lock

  获取最普通的获取锁,如果被其他线程获取,则进行等待

  不会像synchronized一样在异常的时候自动释放锁

  lock方法不能被中断,一旦陷入死锁,lock就会永久等待

package com.jun.juc.lock.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock必须手动释放锁
 */
public class MustUnLock {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        lock.lock();
        try{
            //
            System.out.println(Thread.currentThread().getName()+"-run");
        }finally {
            lock.unlock();
        }
    }
}

  

5.tryLock

  用来尝试获取锁,如果被其他线程占用,则获取成功,返回true,否则返回false,代表获取锁失败

  功能比lock强大了,可以根据是否获取到锁,决定后续程序的行为

  立刻返回

 

6.tryLock(long time, TimeUnit unit)

  超时就放弃

  可以避免死锁

package com.jun.juc.lock.lock;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * tryLock避免死锁
 */
public class TryLockDeadLock implements Runnable {
    int flag = 1;

    static Lock lock1 = new ReentrantLock();
    static Lock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        TryLockDeadLock tryLockDeadLock1 = new TryLockDeadLock();
        TryLockDeadLock tryLockDeadLock2 = new TryLockDeadLock();
        tryLockDeadLock1.flag = 1;
        tryLockDeadLock2.flag = 0;
        new Thread(tryLockDeadLock1).start();
        new Thread(tryLockDeadLock2).start();
    }

    @Override
    public void run() {
        if (flag == 1) {
            try {
                for (int i = 0; i < 100; i++) {
                    if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                        try {
                            System.out.println("1获取了lock1");
                            Thread.sleep(new Random().nextInt(1000));
                            // 获取第二把锁
                            if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("1获取了lock2");
                                    System.out.println("1成功获取两把锁");
                                    break;
                                } finally {
                                    lock2.unlock();
                                    Thread.sleep(new Random().nextInt(1000));
                                }
                            } else {
                                System.out.println("1获取lock2失败,在重试");
                            }
                        } finally {
                            // 因为上面获取到了锁,需要释放
                            lock1.unlock();
                            Thread.sleep(new Random().nextInt(1000));
                        }
                    } else {
                        System.out.println("1获取lock1失败,在重试");
                    }
                }
            } catch (Exception e) {
                // 防止800ms内被中断
                e.printStackTrace();
            }
        }

        if (flag == 0) {
            try {
                for (int i = 0; i < 100; i++) {
                    if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
                        try {
                            System.out.println("2获取了lock2");
                            Thread.sleep(new Random().nextInt(1000));
                            // 获取第二把锁
                            if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("2获取了lock1");
                                    System.out.println("2成功获取两把锁");
                                    break;
                                } finally {
                                    lock1.unlock();
                                    Thread.sleep(new Random().nextInt(1000));
                                }
                            } else {
                                System.out.println("2获取lock1失败,在重试");
                            }
                        } finally {
                            // 因为上面获取到了锁,需要释放
                            lock2.unlock();
                            Thread.sleep(new Random().nextInt(1000));
                        }
                    } else {
                        System.out.println("2获取lock2失败,在重试");
                    }
                }
            } catch (Exception e) {
                // 防止800ms内被中断
                e.printStackTrace();
            }
        }
    }
}

  效果:

Connected to the target VM, address: ‘127.0.0.1:57057‘, transport: ‘socket‘
1获取了lock1
2获取了lock2
1获取lock2失败,在重试
2获取了lock1
2成功获取两把锁
1获取了lock1
1获取了lock2
1成功获取两把锁
Disconnected from the target VM, address: ‘127.0.0.1:57057‘, transport: ‘socket‘

  

7.lockInterruptipy

  把超时时间设置为无限,在过程中,线程可以被中断

package com.jun.juc.lock.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptibly implements Runnable {
    private Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        LockInterruptibly lockInterruptibly = new LockInterruptibly();
        Thread thread = new Thread(lockInterruptibly);
        Thread thread1 = new Thread(lockInterruptibly);
        thread.start();
        thread1.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"尝试获取锁");
        try{
            lock.lockInterruptibly();
            try {
                System.out.println(Thread.currentThread().getName()+"拿到了锁");
                Thread.sleep(5000);
            }catch (Exception e){
                System.out.println("睡眠时间被打断");
            }finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName()+"释放了锁");
            }
        }catch (Exception e){
            System.out.println("等待锁时被打断");
            e.printStackTrace();
        }
    }
}

  效果:

Connected to the target VM, address: ‘127.0.0.1:62438‘, transport: ‘socket‘
Thread-0尝试获取锁
Thread-1尝试获取锁
Thread-0拿到了锁
睡眠时间被打断
Thread-0释放了锁
Thread-1拿到了锁
Disconnected from the target VM, address: ‘127.0.0.1:62438‘, transport: ‘socket‘
Thread-1释放了锁

Process finished with exit code 0

  

8.可见性

  happens-before

  lock拥有可见性保障的

  技术图片

  技术图片

  技术图片

 

 

二:锁的分类

1.分类

  技术图片

 

 

2.悲观锁的劣势

  也叫互斥同步锁

  劣势:

    阻塞和唤醒带来的性能劣势

    永久阻塞,如果持有锁的线程被永久阻塞,那么等待该线程释放的线程,永远都得不到执行

    优先级反转

  

3.悲观锁

  技术图片

 

 

4.乐观锁

  技术图片

 

   技术图片

 

   典型的案例就是原子类,并发容器等

 

5.

  

 

 

 

 

 

 

 

 

 

 

 

 

  

 

以上是关于锁的总结的主要内容,如果未能解决你的问题,请参考以下文章

为啥基于锁的程序不能组成正确的线程安全片段?

关于Java中锁的总结

LockSupport.java 中的 FIFO 互斥代码片段

python常用代码片段总结

BootStrap有用代码片段(持续总结)

java中ReentrantReadWriteLock读写锁的使用