学习笔记Java中的锁Lock

Posted 棉花糖灬

tags:

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

本文参考自:Java并发编程:Lock

Lock锁并不是java内置的功能,其应用场景是在多线程并发访问时,为了避免冲突,需要每个线程先获取锁,避免其他线程的进入,等线程执行完后释放锁,允许其他线程进入。

1. Lock锁与synchronized同步的区别

  • Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
  • Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时,此时Lock的性能要远远优于synchronized。

2. Lock接口的主要方法

Lock是java.util.concurrent.locks包中的一个接口,其主要方法有获取锁的方法:lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly(),释放锁的方法unLock()。

(1) lock()方法

lock()方法用来获取锁,若其他锁被其他线程获取,则等待,直到获取到。所以说一个线程是使用完锁后一定要释放,否则会导致其他线程一直等待。一般会配合try-catch块进行使用,并在finally块中释放锁。

Lock lock = ...;
lock.lock();
try{
    // 处理任务
}catch(Exception ex){
    // 异常处理
}finally{
    lock.unlock(); // 释放锁
}

(2) tryLock()方法

tryLock()方法与lock()方法类似,不同在于前者有返回值,当获取锁成功时则返回true,反之返回false。也就是说这个方法无论能不能获取到锁都会立即返回。

tryLock(long time, TimeUnit unit)方法在tryLock()方法的基础上会等待指定的时间,若时间过后还没获取锁才返回false。两者用法类似。

Lock lock = ...;
if(lock.tryLock()) {
     try{
         // 处理任务
     }catch(Exception ex){
         // 异常处理         
     }finally{
         lock.unlock(); //释放锁
     } 
}else {
    // 如果不能获取锁,则直接做其他事情
}

(3) lockInterruptibly()方法

lockInterruptibly()方法获取锁后允许线程主动终止等待,即若有线程1和线程2两个线程同时使用lockInterruptibly()方法获取锁,且线程1获得了锁,而线程2在等待,那么使用thread2.interrupt()方法能够终止线程2的等待。实际上该方法不仅能打断在等待中的线程,也可以打断已获取锁的线程。该方法无返回值。

Lock lock = ...;
lock.lockInterruptibly();
try{
    // 处理任务
}catch(Exception ex){
    // 异常处理
}finally{
    lock.unlock(); // 释放锁
}

3. 实例

ReentrantLock即可重入锁,它是Lock接口的一个实现类。

public class LockTest {

    public static void main(String[] args) {
        // 可重入锁,Lock接口的一个实现类
        ReentrantLock reentrantLock = new ReentrantLock();

        Runnable runnable = new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                // 获取锁
                reentrantLock.lockInterruptibly();
                try {
                    System.out.println(Thread.currentThread().getName() + "获取了锁");
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 最终都要释放锁
                    reentrantLock.unlock();
                    System.out.println(Thread.currentThread().getName() + "释放了锁");
                }
            }
        };

        Thread thread1 = new Thread(runnable, "Thread1");
        Thread thread2 = new Thread(runnable, "Thread2");
        thread1.start();
        thread2.start();

        try {
            // 主线程睡2s
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 打断thread2,停止等待
        thread2.interrupt();
        System.out.println("Thread2停止等待");
    }
}

其输出为,其中双斜杠后的为注释:

Thread1获取了锁  // 第0秒
Thread2停止等待  // 第2秒
$报错信息$       // 第2秒
Thread1释放了锁  // 第5秒

以上是关于学习笔记Java中的锁Lock的主要内容,如果未能解决你的问题,请参考以下文章

java学习第22天(关于java中的锁LOCK及多线程的面试题)

Java多线程——Lock&Condition

并发包java.util.concurrent.locks.Lock

Java学习笔记—多线程(java.util.concurrent.locks包,转载)

Java多线程与并发库高级应用-工具类介绍

java中ReentrantReadWriteLock读写锁的使用