本文源码:GitHub·点这里 || GitEE·点这里

一、Lock体系结构

1、基础接口简介

Lock加锁相关结构中涉及两个使用广泛的基础API:ReentrantLock类和Condition接口,基本关系如下:

Lock接口

Java并发编程中资源加锁的根接口之一,规定了资源锁使用的几个基础方法。

ReentrantLock类

实现Lock接口的可重入锁,即线程如果获得当前实例的锁,并进入任务方法,在线程没有释放锁的状态下,可以再次进入任务方法,特点:互斥排它性,即同一个时刻只有一个线程进入任务。

Condition接口

Condition接口描述可能会与锁有关联的条件变量,提供了更强大的功能,例如在线程的等待/通知机制上,Conditon可以实现多路通知和选择性通知。

2、使用案例

生产消费模式

写线程向容器中添加数据,读线程从容器获取数据,如果容器为空时,读线程等待。

public class LockAPI01 {private static Lock lock = new ReentrantLock() ;private static Condition condition1 = lock.newCondition() ;private static Condition condition2 = lock.newCondition() ;public static void main(String[] args) throws Exception {List<String> dataList = new ArrayList<>() ;ReadList readList = new ReadList(dataList);WriteList writeList = new WriteList(dataList);new Thread(readList).start();TimeUnit.SECONDS.sleep(2);new Thread(writeList).start();}// 读数据线程static class ReadList implements Runnable {private List<String> dataList ;public ReadList (List<String> dataList){this.dataList = dataList ;}@Overridepublic void run() {lock.lock();try {if (dataList.size() != 2){System.out.println("Read wait...");condition1.await();}System.out.println("ReadList WakeUp...");for (String element:dataList){System.out.println("ReadList:"+element);}condition2.signalAll();} catch (InterruptedException e){e.fillInStackTrace() ;} finally {lock.unlock();}}}// 写数据线程static class WriteList implements Runnable {private List<String> dataList ;public WriteList (List<String> dataList){this.dataList = dataList ;}@Overridepublic void run() {lock.lock();try {dataList.add("Java") ;dataList.add("C++") ;condition1.signalAll();System.out.println("Write over...");condition2.await();System.out.println("Write WakeUp...");} catch (InterruptedException e){e.fillInStackTrace() ;} finally {lock.unlock();}}}
}

这个生产消费模式和生活中的点餐场景极为类似,用户下单,通知后厨烹饪,烹饪完成之后通知送餐。

顺序执行模式

既然线程执行可以互相通知,那也可以基于该机制实现线程的顺序执行,基本思路:在一个线程执行完毕后,基于条件唤醒下个线程。

public class LockAPI02 {public static void main(String[] args) {PrintInfo printInfo = new PrintInfo() ;ExecutorService service =  Executors.newFixedThreadPool(3);service.execute(new PrintA(printInfo));service.execute(new PrintB(printInfo));service.execute(new PrintC(printInfo));}
}
class PrintA implements Runnable {private PrintInfo printInfo ;public PrintA (PrintInfo printInfo){this.printInfo = printInfo ;}@Overridepublic void run() {printInfo.printA ();}
}
class PrintB implements Runnable {private PrintInfo printInfo ;public PrintB (PrintInfo printInfo){this.printInfo = printInfo ;}@Overridepublic void run() {printInfo.printB ();}
}
class PrintC implements Runnable {private PrintInfo printInfo ;public PrintC (PrintInfo printInfo){this.printInfo = printInfo ;}@Overridepublic void run() {printInfo.printC ();}
}
class PrintInfo {// 控制下个执行的线程private String info = "A";private ReentrantLock lock = new ReentrantLock();// 三个线程,三个控制条件Condition conditionA = lock.newCondition();Condition conditionB = lock.newCondition();Condition conditionC = lock.newCondition();public void printA (){try {lock.lock();while (!info.equals("A")) {conditionA.await();}System.out.print("A");info = "B";conditionB.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void printB (){try {lock.lock();while (!info.equals("B")) {conditionB.await();}System.out.print("B");info = "C";conditionC.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void printC (){try {lock.lock();while (!info.equals("C")) {conditionC.await();}System.out.print("C");info = "A";conditionA.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}

该案例经常出现在多线程的面试题中,如何实现ABC的顺序打印问题,基本思路就是基于线程的等待通知机制,但是实现方式很多,上述只是其中一种方式。

二、读写锁机制

1、基础API简介

重入锁的排它特性决定了性能会产生瓶颈,为了提升性能问题,JDK中还有另一套读写锁机制。读写锁中维护一个共享读锁和一个排它写锁,在实际开发中,读的场景还是偏多的,所以读写锁可以很好的提高并发性。

读写锁相关结构中两个基础API:ReadWriteLock接口和ReentrantReadWriteLock实现类,基本关系如下:

ReadWriteLock

提供两个基础方法,readLock获取读机制锁,writeLock获取写机制锁。

ReentrantReadWriteLock

接口ReadWriteLock的具体实现,特点:基于读锁时,其他线程可以进行读操作,基于写锁时,其他线程读、写操作都禁止。

2、使用案例

读写分离模式

通过读写锁机制,分别向数据容器Map中写入数据和读取数据,以此验证读写锁机制。

public class LockAPI03 {public static void main(String[] args) throws Exception {DataMap dataMap = new DataMap() ;Thread read = new Thread(new GetRun(dataMap)) ;Thread write = new Thread(new PutRun(dataMap)) ;write.start();Thread.sleep(2000);read.start();}
}
class GetRun implements Runnable {private DataMap dataMap ;public GetRun (DataMap dataMap){this.dataMap = dataMap ;}@Overridepublic void run() {System.out.println("GetRun:"+dataMap.get("myKey"));}
}
class PutRun implements Runnable {private DataMap dataMap ;public PutRun (DataMap dataMap){this.dataMap = dataMap ;}@Overridepublic void run() {dataMap.put("myKey","myValue");}
}
class DataMap {Map<String,String> dataMap = new HashMap<>() ;ReadWriteLock rwLock = new ReentrantReadWriteLock() ;Lock readLock = rwLock.readLock() ;Lock writeLock = rwLock.writeLock() ;// 读取数据public String get (String key){readLock.lock();try{return dataMap.get(key) ;} finally {readLock.unlock();}}// 写入数据public void put (String key,String value){writeLock.lock();try{dataMap.put(key,value) ;System.out.println("执行写入结束...");Thread.sleep(10000);} catch (Exception e) {System.out.println("Exception...");} finally {writeLock.unlock();}}
}

说明:当put方法一直在睡眠状态时,因为写锁的排它性质,所以读方法是无法执行的。

三、基础工具类

LockSupport简介

LockSupprot定义一组公共静态方法,这些方法提供最基本的线程阻塞和唤醒功
能。

基础方法

park():当前线程阻塞,当前线程被中断或调用unpark方法,park()方法中返回;

park(Object blocker):功能同park(),传入Object对象,记录导致线程阻塞的阻塞对象,方便问题排查;

parkNanos(long nanos):指定时间nanos内阻塞当前线程,超时返回;

unpark(Thread thread):唤醒指定处于阻塞状态的线程;

代码案例

该流程在购物APP上非常常见,当你准备支付时放弃,会有一个支付失效,在支付失效期内可以随时回来支付,过期后需要重新选取支付商品。

public class LockAPI04 {public static void main(String[] args) throws Exception {OrderPay orderPay = new OrderPay("UnPaid") ;Thread orderThread = new Thread(orderPay) ;orderThread.start();Thread.sleep(3000);orderPay.changeState("Pay");LockSupport.unpark(orderThread);}
}
class OrderPay implements Runnable {// 支付状态private String orderState ;public OrderPay (String orderState){this.orderState = orderState ;}public synchronized void changeState (String orderState){this.orderState = orderState ;}@Overridepublic void run() {if (orderState.equals("UnPaid")){System.out.println("订单待支付..."+orderState);LockSupport.park(orderState);}System.out.println("orderState="+orderState);System.out.println("订单准备发货...");}
}

这里基于LockSupport中park和unpark控制线程状态,实现的等待通知机制。

四、源代码地址

GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent

推荐文章:并发编程系列

序号 文章标题
01 Java并发:线程的创建方式,状态周期管理
02 Java并发:线程核心机制,基础概念扩展
03 Java并发:多线程并发访问,同步控制
04 Java并发:线程间通信,等待/通知机制
05 Java并发:悲观锁和乐观锁机制

Java并发编程(06):Lock机制下API用法详解相关推荐

  1. Java 并发编程CAS、volatile、synchronized原理详解

    CAS(CompareAndSwap) 什么是CAS? 在Java中调用的是Unsafe的如下方法来CAS修改对象int属性的值(借助C来调用CPU底层指令实现的): /*** * @param o ...

  2. Java并发编程之锁机制之LockSupport工具

    关于文章涉及到的jdk源码,这里把最新的jdk源码分享给大家----->jdk源码 前言 在上篇文章<Java并发编程之锁机制之AQS(AbstractQueuedSynchronizer ...

  3. 并发编程合集(1)上下文切换详解、死锁及解决方案详解

    目录 <Java 并发编程的艺术>第1章--介绍Java并发编程的挑战 1.1 上下文切换 1.1.1 多线程一定快吗 1.1.2 测试上下文切换次数和时长 1.1.3 如何减少上下文切换 ...

  4. nodejs java rsa_NodeJS加密解密及node-rsa加密解密用法详解

    要用nodejs开发接口,实现远程调用,如果裸奔太危险了,就在网上找了一下nodejs的加密,感觉node-rsa挺不错的,下面来总结一下简单的rsa加密解密用法 初始化环境 新建一个文件夹 node ...

  5. 【Java网络编程与IO流】Http协议详解以及面试有关问题

    HTTP协议详解以及面试有关题目 1 HTTP请求 一个HTTP请求报文由请求行.请求头部.空行和请求数据四个部分组成. 1.1 请求行 请求行中有请求方法字段.URL字段和HTTP协议版本3个字段组 ...

  6. JavaEE基础(02):Servlet核心API用法详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.核心API简介 1.Servlet执行流程 Servlet是JavaWeb的三大组件之一(Servlet.Filter.Listener) ...

  7. Java并发编程:Lock和Synchronized 转

    在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...

  8. Java并发编程实战~Lock

    再造管程的理由 synchronized导致死锁问题,提出了一个破坏不可抢占条件方案,但是这个方案 synchronized 没有办法解决.原因是 synchronized 申请资源的时候,如果申请不 ...

  9. golang:1.并发编程之互斥锁、读写锁详解

    本文转载自junjie,而后稍作修改. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是 ...

最新文章

  1. 在OpenCV中基于深度学习的边缘检测
  2. 我为什么要使用IDE? [关闭]
  3. C# delegate event
  4. [每日电路图] 8、三轴加速度计LIS3DH电路图及功耗等指标
  5. eclipse更新time out的问题
  6. linux修改端口cost值,Linux下通过修改网卡驱动的参数调整Intel网卡的性能
  7. 04_css盒子模型
  8. vb 通过php连接mysql数据库连接_php连接mysql数据库
  9. 高通似乎成了台积电和三星在先进工艺上暗战主角
  10. java 舆情分析_基于Java实现网络舆情分析系统研究与实现.doc
  11. 【游戏开发实战】重温红白机经典FC游戏,顺便教你快速搭建2D游戏关卡(Tilemap | 场景 | 地图)
  12. 华为光猫 HN8346X6 超级管理员密码获取方法,超简单
  13. 如何开启系统打印机服务器,[两种方法]win7系统的打印机服务如何启动?
  14. altium怎么锁定_在AD软件中的锁定与解锁命令应该如何使用?
  15. java基础类库——数字操作类(五)
  16. 干货丨3分钟了解今日头条推荐算法原理(附视频+PPT)
  17. 计算机用户账户不见了,电脑用户账户找不到了怎么处理
  18. forward(转发)与redirect(重定向)的区别
  19. Delphi10.4使用FireDAC数据访问组件开发数据库软件学习开发教程(1)
  20. javaweb网上宠物商城管理系统分前后台(源码+数据库+开题报告+ppt+文档)

热门文章

  1. 计组之中央处理器:1、CPU的功能和基本结构
  2. LeetCode:922. 按奇偶排序数组 II
  3. 二叉树经典题之从前序和中序遍历构建二叉树
  4. 从零开始学PowerShell(4)数据的选择、排序与格式化
  5. Python3bytes、hex、字符串之间相互转换
  6. ovs 下流表port 1进入,port 1出去
  7. 密码与安全新技术总结
  8. Spring的国际化(转载)
  9. 【转载】要有梦想-创造卓越的职业生涯
  10. SolarWinds2002使用说明(簡)