前面说了这个多,我们可以自己尝试实现一个同步器,我们可以简单的参考一下ReentrantLock这个类的实现方式,我们就简单的实现一个不可重入的独占锁吧!

一.简单分析ReentrantLock的结构

  下图所示,直接实现了Lock这个接口,然后定义了一个内部类继承AQS,暂时不考虑公平锁和非公平锁,前面说AQS的时候说过,留有tryAcquire,tryRelease这两个方法在具体子类中根据实际情况实现的,可想而知这个内部类主要的是实现tryAcquire,tryRelease;

  我们看看Lock接口,这些方法就是我们需要实现的;主要是获取锁和释放锁,还有一个实现条件变量的方法;

  这里注意一下,有的方法后面带有Interruptibly这种字样的,这个方法表示如果该线程假如在阻塞队列中挂起了,这时有另外一个线程去调用这个线程的中断方法,那么就会立即抛出异常;不带Interruptibly就是不会对中断进行响应!

  我们如果看看ReentrantLock里面的lock,unlock等方法的实现,可以知道都是调用的Sync的方法,也就是AQS中的一些方法,所以在这里我们可以把Sync看做是一个工具类,我们主要是使用Lock接口的这些方法来实现我们锁的功能;

二.创建一个锁MyNonLock

  我们只需要创建一个类实现Lock类,然后这个类中有一个内部类MySync继承AQS,然后在Lock的那些实现方法中调用MySync对象的某些方法就行了;

package com.example.demo.Lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; public class MyNonLock implements Lock, java.io.Serializable { //创建一个具体的MySync来做具体的工作
private final MySync mySync = new MySync(); @Override
public void lock() {
mySync.acquire(1);
} @Override
public boolean tryLock() {
return mySync.tryAcquire(1);
} @Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return mySync.tryAcquireNanos(1, unit.toNanos(time)); } //带了Interruptibly的方法表示对中断进行响应,就是当一个线程在阻塞队列中被挂起的时候,
//其他线程调用该线程的中断方法中断了该线程,该线程会抛出InterruptedException异常
@Override
public void lockInterruptibly() throws InterruptedException {
mySync.acquireInterruptibly(1);
} @Override
public void unlock() {
mySync.release(1);
} //很方便的获取条件变量
@Override
public Condition newCondition() {
return mySync.newCondition();
} private static class MySync extends AbstractQueuedSynchronizer { // 锁是否已经被持有
protected boolean isHeldExclusively() {
return getState() == 1;
} // 如果state为0,就尝试获取锁,将state修改为1
public boolean tryAcquire(int acquires) {
assert acquires == 1;
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
} // 尝试释放锁,将state设置为0
protected boolean tryRelease(int releases) {
assert releases == 1;
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
} //提供条件变量接口
Condition newCondition() {
return new ConditionObject();
}
} }

三.生产者消费者模式

  我们还可以根据我们自己实现的锁MyNonLock实现一下生产者消费者模式,注意,这个锁是不可重入锁,不需要记录持有锁的线程获取锁的次数,而且state的值为0表示当前锁没有被占用,为1表示已经被占用了;

package com.example.demo.study;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition; import com.example.demo.Lock.MyNonLock; public class Study0202 {
// 我们往这个队列中添加字符串
final static Queue<String> queue = new LinkedBlockingQueue<String>();
// 创建我们自己的锁对象
final static MyNonLock lock = new MyNonLock();
// 当队列queue中字符串满了,其他的生产线程就丢到这个条件队列里面
final static Condition full = lock.newCondition();
// 当队列queue是空的,其余的消费线程就丢到这个条件队列里面
final static Condition empty = lock.newCondition();
// 队列queue中存字符串最多只能是3个
final static int queue_MAX_SIZE = 3; //往队列queue中压入字符串
public static void add() {
lock.lock();
try {
// 当队列满了,就将其他生产线程丢进full的条件队列中
while (queue.size() == queue_MAX_SIZE) {
full.await();
}
System.out.println("prd:" + "hello");
// 往队列queue中添加字符串
queue.add("hello");
// 生产成功,唤醒消费条件队列中的所有线程赶紧去消费
empty.signalAll();
} catch (Exception e) {
//
} finally {
lock.unlock();
}
} //从队列queue弹出字符串
public static void poll() {
lock.lock();
try {
// 当队列queue中一个字符串都没有,就将剩下的消费线程丢进enpty对应的队列中
while (queue.size() == 0) {
empty.await();
}
// 消费队列queue中的字符串
String poll = queue.poll();
System.out.println("consumer:" + poll);
// 消费成功,就唤醒full中所有的生产线程去生产字符串
full.signalAll();
} catch (Exception e) {
//
} finally {
lock.unlock();
}
} public static void main(String[] args) {
// 生产者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
add();
}).start();
} // 消费者线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
poll();
}).start();
}
}
}

  可以看到队列中最多只能是3个字符串,最后都能被消费完毕!

基于AQS自己实现一个同步器的更多相关文章

  1. 老板让只懂Java基本语法的我,基于AQS实现一个锁

    10 点整,我到了公司,又成为全组最后一个到的员工. 正准备刷刷手机摸摸鱼,看见老板神秘兮兮地走了过来. 老板:闪客呀,你写个工具,基于 AQS 实现一个锁,给咱们组其他开发用 我:哦好的 老板:你多 ...

  2. JAVA并发-基于AQS实现自己的显示锁

    一.了解什么是AQS 原文链接:http://www.studyshare.cn/blog-front/blog/details/1131 AQS是AbstractQueuedSynchronizer ...

  3. 聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别

    ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS: AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类 ...

  4. ReentrantLock是如何基于AQS实现的

    ReentrantLock是一个可重入的互斥锁,基于AQS实现,它具有与使用 synchronized 方法和语句相同的一些基本行为和语义,但功能更强大. lock和unlock ReentrantL ...

  5. 基于AQS实现的Java并发工具类

    本文主要介绍一下基于AQS实现的Java并发工具类的作用,然后简单谈一下该工具类的实现原理.其实都是AQS的相关知识,只不过在AQS上包装了一下而已.本文也是基于您在有AQS的相关知识基础上,进行讲解 ...

  6. AQS(抽象队列同步器)

    AQS(全称为AbstractQueuedSynchronizer),即抽象队列同步器,它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列. state的访问方 ...

  7. canal源码之BooleanMutex(基于AQS中共享锁实现)

    在看canal源码时发现一个有趣的锁实现--BooleanMutex 这个锁在canal里面多处用到,相当于一个开关,比如系统初始化/授权控制,没权限时阻塞等待,有权限时所有线程都可以快速通过 先看它 ...

  8. 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)

    搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...

  9. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

随机推荐

  1. 【规范建议】服务端接口返回字段类型与iOS端的解析

    一.本文档的写作目的 App需要跟产品.UI.后台.服务器.测试打交道,app的产出是其他端人员产出的综合体现.与其他端人员沟通就像是开发写接口,也就是面向接口编程的思想. 本文档讲解针对的是服务端返 ...

  2. 关于阿里云ecs服务器无法用FTP进行连接问题

    背景 前两天趁机老马又搞优惠,就又撸了一台三年的ecs来折腾,后来整了半天发现ftp怎么都连接不上,以前也是撸过阿里的服务器,不过启动盘是巨硬家的系统, 最后发现虽然服务器的防火墙关了,但是老马为了安 ...

  3. 分布式事务中间件 TCC-Transaction 源码分析 —— 项目实战

    https://blog.csdn.net/lldouble/article/details/79455172

  4. error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation

    遇到这个问题,请打开项目的Properties(属性)------->Configuration Properties(配置属性)------>C/C++ ------>Prepro ...

  5. hdu 2838 Cow Sorting 树状数组求所有比x小的数的个数

    Cow Sorting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  6. C# Stream篇(三) -- TextWriter 和 StreamWriter

    TextWriter 和 StreamWriter 目录: 为何介绍TextWriter? TextWriter的构造,常用属性和方法 IFormatProvider的简单介绍 如何理解StreamW ...

  7. 一个小白对auth的理解

    ---恢复内容开始--- PS:最近需要做一个验证用户权限的功能,在官方和百度看了下,发现大家都是用auth来做验证,官方有很多auth的使用教程,但是都不全面,我也提问了几个关于auth的问题 也没 ...

  8. CodeBlocks_OpenGL配置

    CodeBlocks版本:13.12 下载OpenGL配置文件 1.glut.dll glut32.dll放入系统盘Windows\System32文件夹 2.glut.h放入CodeBlocks安装 ...

  9. day14-Python运维开发基础(内置函数、pickle序列化模块、math数学模块)

    1. 内置函数 # ### 内置函数 # abs 绝对值函数 res = abs(-10) print(res) # round 四舍五入 (n.5 n为偶数则舍去 n.5 n为奇数,则进一!) 奇进 ...

  10. springboot#配置文件处理

    1. 加载自定义属性文件 2. 通过bean聚合相关属性 1. 在启动类上通过如下注解可以加载自定义的属性文件 @PropertySource(value = {"classpath:pro ...