一. 概述

Lock 是 java.util.concurrent.locks 包 下的接口,Lock 实现提供了比 synchronized 关键字 更广泛的锁操作,它能以更优雅的方式处理线程同步问题。Lock提供了比synchronized更多的功能。

   1.Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是ReentrantLock(可重入锁),ReadWriteLock(读写锁)的代表实现类是ReentrantReadWriteLock

  2.Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。
   
   3.ReadWriteLock 接口以类似方式定义了一些
读取者可以共享而写入者独占的锁。此包只提供了一个实现 ReentrantReadWriteLock

   4 .Lock是可重入锁可中断锁,可以实现公平锁读写锁,写锁为排它锁,读锁为共享锁。ReentrantLock也是一种排他锁 

二.synchronized 与 Lock 的区别

1.synchronized是关键字,是JVM层面的,而Lock是一个接口,是JDK提供的API。

2.当一个线程获取了synchronized锁,其他线程便只能一直等待直至占有锁的线程释放锁。当发生以下情况之一线程才会释放锁:
  a.占有锁的线程执行完了该代码,然后释放对锁的占有。
  b.占有锁线程执行发生异常,此时JVM会让线程自动释放锁。
  c.占有锁线程进入 WAITING 状态从而释放锁,例如在该线程中调用wait()方法等。

   但是如果占有锁的线程由于要等待IO或者因为其他原因(比如调用sleep方法)而使线程阻塞了,但是又没有释放锁,那么线程就只能一直等待,那么这时我们可能需要一种可以不让线程无期限的等待下去的方法,比如只等待一定的时间(tryLock(long time, TimeUnit unit)或者能被人为中断lockInterrup0tibly(),这种情况我们需要Lock。

3.当多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作也会发生冲突现象,但是读操作和读操作不会发生冲突现象,但是如果采用synchronized进行同步的话,就会导致当多个线程都只是进行读操作时也只有获取锁的线程才能进行读操作,其他线程只能等待锁释放后才能读,Lock则可以实现当多个线程都只是进行读操作时,线程之间不会发生冲突,例如:ReentrantReadWriteLock()。

4.可以通过Lock得知线程有没有成功获取到锁 (例如:ReentrantLock) ,但这个是synchronized无法办到的。

5.锁属性上的区别:synchronized是不可中断锁和非公平锁,ReentrantLock可以进行中断操作并别可以控制是否是公平锁

6.synchronized能锁住方法和代码块,而Lock只能锁住代码块。

7.synchronized无法判断锁的状态,而Lock可以知道线程有没有拿到锁。

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

三.Lock接口

Lock是一个接口,接口的实现类有 ReentrantLock和内部类ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock,该章节所描述的皆为 ReentrantLock

public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();
}

其中 lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的,unLock()方法是用来释放锁的 。

1. lock()

用来获取锁。如果锁已被其他线程获取,则进行等待。采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

    private static Lock lock = new ReentrantLock();public static void main(String[] args) {lock.lock();try{System.out.println("获取锁成功!!");}catch(Exception e){e.printStackTrace();}finally{System.out.println("释放锁成功");lock.unlock();}}

2. tryLock():

用来尝试获取锁,但是该方法是有返回值的,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回,在拿不到锁时也不会一直在那等待。

    private static Lock lock = new ReentrantLock();public static void main(String[] args) {if(lock.tryLock()) {try{System.out.println("成功获取锁!!");}catch(Exception e){e.printStackTrace();}finally{lock.unlock();}}else {System.out.println("未获取锁,先干别的");}}

3. tryLock(long time, TimeUnit unit)

和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

    private static Lock lock = new ReentrantLock();public static void main(String[] args) {try{if(lock.tryLock(5000, TimeUnit.MILLISECONDS)) {System.out.println("成功获取锁!!");}else {System.out.println("未获取锁,先干别的");}}catch(Exception e){e.printStackTrace();}finally{lock.unlock();}}

4.lockInterruptibly()

当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

package ReentrantLockTest;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Test {private Lock lock = new ReentrantLock();public static void main(String[] args) {Test test = new Test();MyThread a = new MyThread(test);MyThread b = new MyThread(test);a.start();b.start();b.interrupt();}public void insert(Thread thread) throws InterruptedException {//注意:如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出lock.lockInterruptibly();try {System.out.println(thread.getName() + "得到了锁");Thread.sleep(3000);} finally {lock.unlock();System.out.println(thread.getName() + "释放了锁");}}static class MyThread extends Thread {private Test test;public MyThread(Test test) {this.test = test;}@Overridepublic void run() {try {test.insert(Thread.currentThread());} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "被中断");}}}}

注意:当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,才可以响应中断。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只能一直等待下去。

5.Condition接口和newCondition()方法

synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类也可以借助于Condition接口与newCondition()方法。

synchronized关键字在使用notify/notifyAll()方法进行通知时,被通知的线程是由JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知”。

synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在该一个实例上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程,这样会造成很大的效率问题,而Condition可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。从而可以有选择性的进行线程通知,在调度线程上更加灵活。

    //使当前线程在接到信号或被中断之前一直处于等待状态。void await();//使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。boolean await(long time, TimeUnit unit);//使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。long awaitNanos(long nanosTimeout);//使当前线程在接到信号之前一直处于等待状态。void awaitUninterruptibly();//使当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。boolean awaitUntil(Date deadline);//唤醒一个等待线程。void signal();//唤醒所有等待线程。void signalAll();

5.1 Condition实现等待/通知机制

当调用 await() 语句后,线程将被阻塞,必须执行完signal()所在的try语句块之后才释放锁,condition.await()后的语句才能被执行。

package ReentrantLockTest;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Test {public static void main(String[] args) throws InterruptedException {MyService service = new MyService();MyThread a = new MyThread(service);a.start();Thread.sleep(3000);service.signal();}static public class MyService {private Lock lock = new ReentrantLock();public Condition condition = lock.newCondition();public void await() {lock.lock();try {System.out.println("准备调用condition.await()方法,将该线程阻塞");condition.await();System.out.println("已调用condition.await()方法,此时已被 signal() 方法唤醒");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void signal() {lock.lock();try {System.out.println("准备调用condition.signal()方法");condition.signal();Thread.sleep(3000);System.out.println("已调用condition.signal()方法,去唤醒 await() 方法");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}static public class MyThread extends Thread {private MyService service;public MyThread(MyService service) {this.service = service;}@Overridepublic void run() {service.await();}}}

 输出:

准备调用condition.await()方法,将该程序阻塞
准备调用condition.signal()方法
已调用condition.signal()方法,去唤醒 await() 方法
已调用condition.await()方法,此时已被 signal() 方法唤醒

5.2 多个Condition实例实现等待/通知机制

一个Lock对象中可以创建多个Condition实例,调用某个实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。

package ReentrantLockTest;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockTest {private Lock lock = new ReentrantLock();private Condition conditionA = lock.newCondition();private Condition conditionB = lock.newCondition();public void awaitA() {lock.lock();try {System.out.println("准备调用conditionA.await()方法,将该线程阻塞");conditionA.await();System.out.println(" awaitA 已被唤醒");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void awaitB() {lock.lock();try {System.out.println("准备调用conditionB.await()方法,将该线程阻塞");conditionB.await();System.out.println(" awaitB 已被唤醒");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void signalA() {lock.lock();try {System.out.println("准备唤醒 conditionA 下的所有线程");conditionA.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void signalB() {lock.lock();try {System.out.println("准备唤醒 conditionB 下的所有线程");conditionB.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
package ReentrantLockTest;public class Test {public static void main(String[] args) throws InterruptedException {LockTest lockTest = new LockTest();ThreadA a = new ThreadA(lockTest);a.setName("A");a.start();ThreadB b = new ThreadB(lockTest);b.setName("B");b.start();Thread.sleep(3000);lockTest.signalA();}static public class ThreadA extends Thread {private LockTest lockTest;public ThreadA(LockTest lockTest) {this.lockTest = lockTest;}@Overridepublic void run() {lockTest.awaitA();}}static public class ThreadB extends Thread {private LockTest lockTest;public ThreadB(LockTest lockTest) {this.lockTest = lockTest;}@Overridepublic void run() {lockTest.awaitB();}}
}

输出: 

准备调用conditionA.await()方法,将该线程阻塞
准备调用conditionB.await()方法,将该线程阻塞
准备唤醒 conditionA 下的所有线程awaitA 已被唤醒

四.ReadWriteLock接口

public interface ReadWriteLock {// 读锁Lock readLock();// 写锁Lock writeLock();
}

ReentrantLock是一种排他锁,同一时刻只允许一个线程访问,ReadWriteLock 接口的实现类 ReentrantReadWriteLock 读写锁提供了两个方法:readLock()和writeLock()用来获取读锁和写锁,也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。

读写锁维护了两个锁,一个是读操作相关的锁也称为共享锁,一个是写操作相关的锁也称为排他锁。通过分离读锁和写锁,其并发性比一般排他锁有了很大提升。

多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥(只要出现写操作的过程就是互斥的)。在没有线程进行写操作时,进行读取操作的多个线程都可以获取读锁,而进行写入操作的线程只有在获取写锁后才能进行写操作。即多个线程可以同时进行读取操作,但是同一时刻只允许一个线程进行写入操作。

1.读锁

package ReentrantLockTest;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReentrantReadWriteLockTest {private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();public static void main(String[] args) {final ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest();new Thread() {public void run() {test.get(Thread.currentThread());}}.start();new Thread() {public void run() {test.get(Thread.currentThread());}}.start();}public void get(Thread thread){reentrantReadWriteLock.readLock().lock();try {for (int i=0;i<10;i++){System.out.println(thread.getName() + "正在进行读操作");Thread.sleep(1000);}System.out.println(thread.getName() + "读操作完毕");} catch (InterruptedException e) {e.printStackTrace();} finally {reentrantReadWriteLock.readLock().unlock();}}}

输出:

Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1读操作完毕
Thread-0读操作完毕

多个线程可以同时获得读锁

2.读写互斥

package ReentrantLockTest;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReentrantReadWriteLockTest {public static void main(String[] args) {LockTest lockTest = new LockTest();ReadThread readThread = new ReadThread(lockTest);ReadThread readThread2 = new ReadThread(lockTest);WriteThread writeThread = new WriteThread(lockTest);ReadThread readThread3 = new ReadThread(lockTest);readThread.start();readThread2.start();writeThread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}readThread3.start();}static public class LockTest {private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();public void read(Thread thread) {lock.readLock().lock();try {for (int i=0;i<5;i++){System.out.println(thread.getName() + "正在进行读操作");Thread.sleep(1000);}System.out.println(thread.getName() + "读操作完毕");} catch (Exception e) {e.printStackTrace();} finally {lock.readLock().unlock();}}public void write(Thread thread) {lock.writeLock().lock();try {for (int i=0;i<5;i++){System.out.println(thread.getName() + "正在进行写操作");Thread.sleep(1000);}System.out.println(thread.getName() + "写操作完毕");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.writeLock().unlock();}}}static public class ReadThread extends Thread {private LockTest lockTest;public ReadThread(LockTest lockTest) {this.lockTest = lockTest;}@Overridepublic void run() {lockTest.read(Thread.currentThread());}}static public class WriteThread extends Thread {private LockTest lockTest;public WriteThread(LockTest lockTest) {this.lockTest = lockTest;}@Overridepublic void run() {lockTest.write(Thread.currentThread());}}
}

输出:

Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1读操作完毕
Thread-0读操作完毕
Thread-2正在进行写操作
Thread-2正在进行写操作
Thread-2正在进行写操作
Thread-2正在进行写操作
Thread-2正在进行写操作
Thread-2写操作完毕
Thread-3正在进行读操作
Thread-3正在进行读操作
Thread-3正在进行读操作
Thread-3正在进行读操作
Thread-3正在进行读操作
Thread-3读操作完毕

由此可以看出,读锁可以共享,写锁只有在所有读锁释放后才能执行,但是当写锁在阻塞和获取过程中,之后的读锁也会阻塞,需要等到写锁释放后才能获取。

java中的Lock锁相关推荐

  1. Java 中的Lock锁

    Lock锁,可以得到和 synchronized一样的效果,即实现原子性.有序性和可见性. 相较于synchronized,Lock锁可手动获取锁和释放锁.可中断的获取锁.超时获取锁. Lock 是一 ...

  2. java lock unlock_详解Java中的ReentrantLock锁

    ReentrantLock锁 ReentrantLock是Java中常用的锁,属于乐观锁类型,多线程并发情况下.能保证共享数据安全性,线程间有序性 ReentrantLock通过原子操作和阻塞实现锁原 ...

  3. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等...

    http://blog.51cto.com/13919357/2339446 Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容 ...

  4. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互 ...

  5. java volatile lock_Java并发学习笔记 -- Java中的Lock、volatile、同步关键字

    Java并发 一.锁 1. 偏向锁 1. 思想背景 来源:HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同 一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁 ...

  6. 浅谈Java中的各种锁

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级 ...

  7. Java中的各种锁事

    本文来聊下Java中的各种锁 文章目录 锁概述 各种锁描述 本文小结 锁概述 本文来聊下Java中的各种锁,彻底理解Java中的各种锁. Java中的各种锁 序号 锁名称 应用 1 乐观锁 CAS 2 ...

  8. java 自旋锁_搞懂Java中的自旋锁

    轻松搞懂Java中的自旋锁 前言 在之前的文章<一文彻底搞懂面试中常问的各种"锁">中介绍了Java中的各种"锁",可能对于不是很了解这些概念的同学 ...

  9. Java中的互斥锁介绍

    前言   互斥锁是一种广泛应用于多线程编程中的并发控制机制.在Java中,互斥锁有很多不同的实现方式,在本文中我们将介绍Java中常见的几种互斥锁实现方式,并讲解它们的用法.原理和代码案例. sync ...

最新文章

  1. Windows 服务入门指南
  2. TurnipBit:可以带着孩子一起玩编程的MicroPython开发板!
  3. 服务器新增svn 文件,公网的SVN服务器,批量新增文件会报错
  4. I​n​n​o​ ​s​e​t​u​p​ ​常​用​修​改​技​巧
  5. 打响进军元宇宙第一枪!网易云信发布两大元宇宙解决方案
  6. 如题,用C#语言 如何给下拉列表框动态添加数据?,C#复习题 单项选择题
  7. 辅助出售网站源码_出售网站意味着出售社区
  8. fork()函数_UNIX环境高级编程(APUE)系列学习第8章-2 exit系列函数与wait系列函数...
  9. 2019美赛C题o奖论文结构整理
  10. python二级考试选择题公共基础知识_计算机二级选择题(公共基础知识)
  11. 纯Css比较好看的中英文字体样式(持续整理版)
  12. 测试用例设计方法_场景法(游戏向)
  13. QT开发应用程序(17)-- 读写XLS文件
  14. APP运营推广人员必备通讯录
  15. 自动作诗器 二逼青年立马变文艺青年~~
  16. mtk,展讯等手机平台知识杂烩
  17. 跟着黑马程序员pink老师学习的笔记及小破站学习的笔记
  18. 哄女孩子上床的方法,不看你会后悔莫及!
  19. IT基础设施:CentOS安装
  20. 【转载】浅谈米之思想

热门文章

  1. C语言#pragma使用
  2. 造梦西游online十殿阎罗篇:法宝篇(莫等闲,空悲切)
  3. ISP和DSP的区别
  4. Cadence PCB 仿真宝典【目录】
  5. C# SolidWorks二次开发-工程图-遍历选中视图中的草图
  6. 暑假matlab最后一次训练(编程题)碎纸片的拼接复原(前2题)
  7. 折线分割平面 ---递推 记录
  8. 常见地图服务以及区别
  9. Speech and Language Processing之Part-of-Speech Tagging
  10. java 打印gc_输出Java的GC信息