前面说了这个多,我们可以自己尝试实现一个同步器,我们可以简单的参考一下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. 关于java自学的内容以及感受(7.21)

    直接切入正题说一下自学到的内容: 定义合法标识符的规则: 可以由英文字母,数字,_,$组成. 不能数字开头和包含空格. 不可以使用关键字和保留字,但是可以包含关键字和保留字. byte short i ...

  2. 在javaweb中对于session的使用

    1.初次调用session时: String username="student"; HttpSession session=request.getSession(true);// ...

  3. 区间树Splay——[NOI2005]维护数列

    无指针Splay超详细讲解 区间树这玩意真TM玄学. 学这东西你必须要拥有的 1.通过[模板]文艺平衡树(Splay),[模板]普通平衡树,GSS3 - Can you answer these qu ...

  4. 笔记-Python-性能优化

    笔记-Python-性能优化 1.      开始 1.1.    python性能差么? 做一个判断前,先问是不是. python运行效率低是事实. 1.2.    为什么? 原因: Python是 ...

  5. static在c\c++中的作用(翁恺c++公开课[28-29]学习笔记)

    static相对来说是一个较复杂的修饰符,c++中的static在c的基础之上又包含了static在类中的应用(也就是说多了static的成员变量和static的成员函数):c\c++中静态变量.对象 ...

  6. JS 数组相关

    数组 function SelectLoadTable() { var mids = $("#mids").val(); var halls = $("#halls&qu ...

  7. 设计模式课程 设计模式精讲 12-2 适配器模式coding

    1 重要 1.1 类适配器和对象适配器最大的区别 2 代码演练 2.1 代码演练1(类适配器模式) 2.2 代码演练2(对象适配模式) 2.3 代码演练3(具体应用场景) 1 重要 1.1 类适配器和 ...

  8. requests---requests封装请求类型

    我们在做自动化的时候,肯定是代码越简洁越好,代码重复量越少越好,这里呢,我们可以通过把requests的请求类型都封装起来,这样编写用例的时候可以直接进行请求 requests方法封装 我们通常用的最 ...

  9. 去除input边框 input去除边框 去除input获取焦点时的蓝色外边框

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. java swing简介

    java应用程序用户界面开发包 Swing是一个用于开发Java应用程序用户界面的开发工具包.它以抽象窗口工具包(AWT)为基础使跨平台应用程序可以使用任何可插拔的外观风格.Swing开发人员只用很少 ...