java线程基础巩固---如何实现一个自己的显式锁Lock
拋出synchronized问题:
对于一个方法上了同锁如果被一个线程占有了,而假如该线程长时间工作,那其它线程不就只能傻傻的等着,而且是无限的等这线程工作完成了才能执行自己的任务,这里来演示一下这种场景:

上面代码就是开启了两个线程,执行顺序有先后,分别执行这个同步方法,看下结果:

对于线程的中止其实是有系统API的,咱们可以试验一上试图打断t2线程不让他傻傻的一直等着了:

看结果:

因为同步代码块中是一个while的死循环,接收不到中断异常的,所以基于这个场景咱们需要自己来定义一个带有超时功能的锁,下面开始。
不包括超时的Lock:
其实在java5的并发包中有专门的Lock,这里校仿一下系统的实现,先新建一个接口,将其锁的行为给定义一下:

然后新建一个具体类来实现该接口:

下面先来实现lock()方法,这里先定义两个成员变量:

首先判断是否锁被其它线程给占用了,如果占用了将当前线程加入阻塞集合中:

但!!!目前代码是有问题的,根据之前所学对于wait()是一定得要有一个monitor才行,否则肯定会报错,所以:

当这个循环条件退出了那证明就拿到了锁,所以接着处理正常的情况:

接下来实现unlock方法,比较容易理解直接贴出代码:

然后再将阻塞的查询方法给实现:

但是此次有一个细节问题:就是getBlockedThreads方法是直接将这个集合给返回给调用者了,此时调用者是可以修改Lock里面的blockedThreadCollection对象的,所以目前这样直接返回不太安全,应该包装一下不能让调用者来修改它,如下:

看一下官方的说明:

好,接下来使用一下咱们自己写的Lock,如下:

编译运行:

呃,抛异常了,在unlock()方法中的notifyAll(),它跟wait()方法一样也是需要一个monitor,所以修改代码如下:

再次运行:

这样咱们自己就定义了一个跟syncronized关键字类似的效果,只有一个线程执行完了其它线程才有机会去工作,不过目前的Lock还有一个问题,就是调用者可以随意的调用咱们封装的unlock()方法,而造成的影响嘛,下面做实验来说明一下:

看结果:

所以~~拉下来解决这个问题,其实解决也很简单,在Lock中加入一个变量用来记录lock()的线程,然后在unlock()时做一个判断,具体做法如下:
/**
* 以boolean来实现锁
*/
public class BooleanLock implements Lock { //如果为true代表已经被其它线程抢到锁了;否则代表锁是空闲的还木有被其它线程占有
private boolean initValue;
//当前拿到锁的线程,以便用来控制释放锁状态问题
private Thread currentThread; //阻塞的线程集合
private Collection<Thread> blockedThreadCollection = new ArrayList<>(); @Override
public synchronized void lock() throws InterruptedException {
while (initValue) {//说明锁已经被其它线程占用了,此时让线程进入等待状态
blockedThreadCollection.add(Thread.currentThread());
this.wait();
}
blockedThreadCollection.remove(Thread.currentThread());
this.initValue = true;
currentThread = Thread.currentThread();
} @Override
public void lock(long timeOutMills) throws InterruptedException, TimeOutException { } @Override
public synchronized void unlock() {
if (Thread.currentThread() == currentThread) {
this.initValue = false;
Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
this.notifyAll();
}
} @Override
public Collection<Thread> getBlockedThreads() {
return Collections.unmodifiableCollection(blockedThreadCollection);
} @Override
public int getBlockedSize() {
return blockedThreadCollection.size();
}
}
编译运行:

就成功修复了这个BUG。
超时的Lock:
好,如文篇描述开头所抛出的synchronized所存在问题,这里通过咱们自己的写的锁来解决一下,当然此时就需要实现Lock中最后一个木有实现的方法了,如下:

那如何实现呢?其逻辑也比较简单,这里就不多解释了,直接贴代码:
/**
* 以boolean来实现锁
*/
public class BooleanLock implements Lock { //如果为true代表已经被其它线程抢到锁了;否则代表锁是空闲的还木有被其它线程占有
private boolean initValue;
//当前拿到锁的线程,以便用来控制释放锁状态问题
private Thread currentThread; //阻塞的线程集合
private Collection<Thread> blockedThreadCollection = new ArrayList<>(); @Override
public synchronized void lock() throws InterruptedException {
while (initValue) {//说明锁已经被其它线程占用了,此时让线程进入等待状态
blockedThreadCollection.add(Thread.currentThread());
this.wait();
}
blockedThreadCollection.remove(Thread.currentThread());
this.initValue = true;
currentThread = Thread.currentThread();
} @Override
public synchronized void lock(long timeOutMills) throws InterruptedException, TimeOutException {
if (timeOutMills <= 0)//先做一个容错处理
lock();
long hasRemaining = timeOutMills;
long endTime = System.currentTimeMillis() + timeOutMills;
while (initValue) {
if (hasRemaining <= 0)
throw new TimeOutException("Time out");
blockedThreadCollection.add(Thread.currentThread());
this.wait(timeOutMills);//此时需要调用带参数的wait()方法啦
hasRemaining = endTime - System.currentTimeMillis();
}
blockedThreadCollection.remove(Thread.currentThread());
this.initValue = true;
currentThread = Thread.currentThread();
} @Override
public synchronized void unlock() {
if (Thread.currentThread() == currentThread) {
this.initValue = false;
Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
this.notifyAll();
}
} @Override
public Collection<Thread> getBlockedThreads() {
return Collections.unmodifiableCollection(blockedThreadCollection);
} @Override
public int getBlockedSize() {
return blockedThreadCollection.size();
}
}
接着来改一下测试代码:

编译运行:


java线程基础巩固---如何实现一个自己的显式锁Lock的更多相关文章
- Java 实现一个自己的显式锁Lock(有超时功能)
Lock接口 package concurency.chapter9; import java.util.Collection; public interface Lock { static clas ...
- “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)
Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...
- “全栈2019”Java多线程第三十一章:中断正在等待显式锁的线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- java之AQS和显式锁
本次内容主要介绍AQS.AQS的设计及使用.ReentrantLock.ReentrantReadWriteLock以及手写一个可重入独占锁 1.什么是AQS? AQS,队列同步器AbstractQu ...
- Java并发-显式锁篇【可重入锁+读写锁】
作者:汤圆 个人博客:javalover.cc 前言 在前面并发的开篇,我们介绍过内置锁synchronized: 这节我们再介绍下显式锁Lock 显式锁包括:可重入锁ReentrantLock.读写 ...
- Java线程基础知识(状态、共享与协作)
1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...
- Java 线程基础知识
前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...
- Java 线程基础
Java 线程基础
随机推荐
- 字符编码笔记:ASCII,Unicode 和 UTF-8(理解)
1.ASCII 码 美国制定的字符编码规则,对英语字符与二进制位之间的关系做了统一规定. 占一个字节,8 位,最多可表示 2^8 = 256 种状态(字符) 实际共有 128 个字符,只占用一个字节的 ...
- Linux磁盘文件系统与格式化实战(一)
fdisk分区的实质: 用fdisk分区的实质,就是修改0磁头0磁道1扇区的前446字节之后的64字节的分区表信息. 问题:可以使用fdisk分区的磁盘大小必须小于2T,如果大于2T呢,分区就用par ...
- odoo12安装指南
声明:本指南默认已安装好Python3和pycharm及postgresql,odoo12的源码包 一. 1.在pycharm创建一个新的项目 建议创建在虚拟环境中 2.在pycharm的控制台下检验 ...
- VS2013:error C1069: 无法读取编译器命令行
前一阵搞python和matlab,没用VS 2013,今天打开一个C++程序想跑一跑,突然蹦出这么个错误,然后发现电脑上所有的程序都会这样了. 后来发现是TMP/TEMP环境变量路径有空格的问题,更 ...
- CentOS7.0 测试环境准备过程
1. 公司最近开始走向国产化和开源路线,因为绝大多数国产化都是采取的linux路线, 所以为了保证兼容性, 测试环境大部分从windows迁移到了linux环境. 测试采取逐步推进的模式, 先测试兼容 ...
- bat命令教程
转自:https://www.jb51.net/article/151923.htm 第一章 批处理基础 第一节 常用批处理内部命令简介 批处理定义:顾名思义,批处理文件是将一系列命令按一定的顺序集合 ...
- 转载:同一台电脑教你配置多个Tomcat的环境变量
装两个tomcat 分别是6.0和7.0 可想运行tomcat6.0 但是实际上却运行tomcat7.0 两个版本都是用解压缩包 其实就是不能运行tomcat6.0 只能运行7.0 两个环境变量都配置 ...
- 【jmeter测试范例】001——TCP测试
1.打开Jmeter(或者运行NewDriver.java启动Jmeter) 2.新建一个测试计划 ······ 3.新建线程组 4.设置线程组的参数 1.线程的数量 2.要在多久内完成,即每个请求发 ...
- Netty的学习
看了几天高并发和NIO 今晚终于要开始学习Netty http://ifeve.com/netty5-user-guide/ Netty实现通信的步骤 1.创建两个NIO线程组,一个专门用于网络事件的 ...
- IPv4-构造超网
5台PC和两个路由器 PC 设置 IP地址 子网掩码 默认网关 路由器设置 接口的IP地址 子网掩码 static(网络 掩码 下一跳) PC1 ping ...