Java锁原理学习

为了学习Java锁的原理,参照ReentrantLock实现了自己的可重入锁,代码如下:

先上AQS的相关方法:

// AQS = AbstractQueuedSynchronizer, 抽象队列同步器

// 它提供了对资源的占用、释放,线程的等待、唤醒等接口和具体实现

// 它维护了一个volatile int state来代表共享资源的状态,和一个FIFO线程等待队列

// 获取排它锁

// 先尝试获取锁,如果获取不到则添加到等待队列

// 等待队列通过 LockSupport.park(this) 实现等待

// 通过 LockSupport.unpark(s.thread) 实现唤醒

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

// 默认的tryAcquire抛出异常,需要子类实现

protected boolean tryAcquire(int arg) {

throw new UnsupportedOperationException();

}

// 释放锁

// 尝试释放,如果成功则唤醒等待的线程

public final boolean release(int arg) {

if (tryRelease(arg)) {

Node h = head;

if (h != null && h.waitStatus != 0)

unparkSuccessor(h);

return true;

}

return false;

}

// 默认的tryRelease抛出异常,需要子类实现

// 返回值true时表示当前线程已完全释放该锁(因为可重入所以需要多次release),否则返回false

protected boolean tryRelease(int arg) {

throw new UnsupportedOperationException();

}

// 等待条件锁

public final void await() throws InterruptedException {

// 1. 保存当前线程的锁状态(即state的值)

// 2. 调用 LockSupport.park(this); 实现等待

// 3. 被唤醒后重新尝试获取锁

}

// 条件等待唤醒,仅唤醒等待队列的第一个线程

public final void signal() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignal(first);

}

// 条件唤醒,唤醒等待队列的所有线程

public final void signalAll() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignalAll(first);

}

// signal()和signalAll()最终都调用该方法唤醒特定线程

final boolean transferForSignal(Node node) {

// LockSupport.unpark(node.thread);

}

以下是自己实现的MyLock:

public static class MyLock extends AbstractQueuedSynchronizer {

/**

*

*/

private static final long serialVersionUID = 1L;

// 加锁方法通过aqs的acquire方法实现,样例仅实现排它锁功能

public void lock() {

acquire(1);

}

// 尝试获取锁,子类需实现该方法,核心是设置aqs中state的值

@Override

protected boolean tryAcquire(int n) {

int c = getState();

Thread t = Thread.currentThread();

// 如果c为0则表示当前锁没有被占用

if (c == 0) {

// 如果没有前序线程,则通过CAS尝试设置state值为申请的资源数

if (!hasQueuedPredecessors() && compareAndSetState(0, n)) {

// 如果获取锁成功则需要记录当前持有锁的线程,用于后续可重入和释放锁

setExclusiveOwnerThread(t);

return true;

}

// 如果当前线程已持有该锁则直接更新state值为总的资源数

} else if (t == getExclusiveOwnerThread()) {

setState(c + n);

return true;

}

return false;

}

// 释放锁通过aqs的release方法实现

public void unlock() {

release(1);

}

// 尝试释放锁,子类需实现该方法

// 首先判断当前线程是否持有该锁,否则抛出异常

// 更新state的值,如果已完全释放则设置当前持有排它锁的线程为null并返回true,否则返回false

@Override

protected boolean tryRelease(int n) {

if (getExclusiveOwnerThread() != Thread.currentThread()) {

throw new IllegalMonitorStateException();

}

int c = getState() - n;

setState(c);

if (c == 0) {

setExclusiveOwnerThread(null);

}

return c == 0;

}

// 创建条件锁

// 条件等待:cond.await();

// 条件满足通知:cond.signal(); 或cond.signalAll();

public Condition newCondition() {

return new ConditionObject();

}

@Override

protected boolean isHeldExclusively() {

return this.getExclusiveOwnerThread() == Thread.currentThread();

}

}

测试程序:

public static void main(String[] args) {

MyLock lock = new MyLock();

// 使用MyLock或ReentrantLock结果相同

// ReentrantLock lock = new ReentrantLock();

Condition c = lock.newCondition();

Runnable run = () -> {

String n = Thread.currentThread().getName();

lock.lock();

lock.lock();

System.err.println("i am " + n);

try {

if (n.equals("t1")) {

c.await();

} else {

c.signal();

}

} catch (InterruptedException e) {

e.printStackTrace();

}

lock.unlock();

lock.unlock();

System.err.println(n + " finished");

};

new Thread(run, "t1").start();

new Thread(run, "t2").start();

}

// 输出

i am t1

i am t2

t2 finished

t1 finished

以下补充Semaphore的原理:

Semaphore是一个计数信号量,必须由获得它的线程释放,常用于限制可以访问资源的线程数量。

// 用法

// 初始化指定数量的许可证

Semaphore s = new Semaphore(2);

// 需要获取的许可证数量

s.acquire(2);

// 需要释放的许可证数量

s.release(2);

// 实现

public class Semaphore {

private final Sync sync;

// 通过AQS实现

abstract static class Sync extends AbstractQueuedSynchronizer {

Sync(int permits) {

setState(permits);

}

}

// 获取直接调用AQS的acquire方法

public void acquire(int permits) throws InterruptedException {

if (permits < 0) throw new IllegalArgumentException();

sync.acquireSharedInterruptibly(permits);

}

// 释放同样直接调用AQS的release方法

public void release(int permits) {

if (permits < 0) throw new IllegalArgumentException();

sync.releaseShared(permits);

}

}

java锁原理_Java锁原理学习相关推荐

  1. java锁实现_Java锁实现

    java锁实现 我们都将第三方库用作开发的正常部分. 通常,我们无法控制其内部. JDK随附的库是一个典型示例. 这些库中的许多库都使用锁来管理争用. JDK锁具有两种实现. 一个使用原子CAS样式指 ...

  2. java 全局变量 加锁_Java锁机制(一)synchronized

    进行多线程编程的时候,需要考虑的是线程间的同步问题.对于共享的资源,需要进行互斥的访问.在Java中可以使用一些手段来达到线程同步的目的: 1. synchronized 2. ThreadLocal ...

  3. java并发进程共享变量_JAVA并发编程学习:共享对象

    可见性 在一个单线程程序中,如果向一个变量先写入值,然后在没有写干涉的情况下读取这个变量,会得到相同的返回值.但是当读和写发生在不同的线程中时,就不能保证读线程及时地读取其他线程写入的值.在JAVA中 ...

  4. java linkedlist实例_Java Linkedlist原理及实例详解

    这篇文章主要介绍了Java Linkedlist原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 定义:linkedlist属于链表结构 ...

  5. java nio 事件_Java NIO原理及实例

    Java NIO是在jdk1.4开始使用的,它既可以说成"新I/O",也可以说成非阻塞式I/O.下面是java NIO的工作原理: 1. 由一个专门的线程来处理所有的 IO 事件, ...

  6. java thread类_Java多线程原理及Thread类详解

    多线程原理 代码如下: 自定义线程类: 测试类: 流程图: 程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建.随着调用mt的对象的start方法,另外一 ...

  7. java数组原理_Java数组排序原理

    Arrays排序原理 Java Arrays排序原理 计数排序源码 (short为例) ​ for (int i = left - 1; ++i <= right;count[a[i] - Sh ...

  8. java 匿名内部类 参数_Java匿名内部类原理与用法详解

    本文实例讲述了Java匿名内部类原理与用法.分享给大家供大家参考,具体如下: 一 点睛 匿名内部类适合创建那种只需要一次使用的类,定义匿名内部类的语法格式如下: new 父类构造器(实参列表) | 实 ...

  9. java 断点续传 开源_java断点续传原理

    先说说断点续传的原理:这是HTTP 1.1协议的一部分,并不需要客户端特意去做多么复杂的事情.以前我曾经看过一个单位的技术标书,其中有下载的断点续传这一要求,给出的offer居然还挺高的... 简单的 ...

最新文章

  1. 数据分析必备:掌握这个R语言基础包1%的功能让你事半功倍!(附代码)
  2. centos mysql拒绝连接失败_CentOS下mysql远程连接的失败的解决方法
  3. 历时八年 HTML5标准终于制定完成
  4. linux命令find
  5. 学习Docker从小白到入门
  6. springboot拦截请求路径_SpringBoot整合Ant Design Pro进行部署
  7. C# XML反序列化与序列化举例:XmlSerializer(转)
  8. SQLServer中round函数
  9. linux command read the content,Linux while 和 read 的用法
  10. PAT 1086 就不告诉你
  11. 用Java实现md5加密
  12. 判断一个数是否是素数
  13. 渗透测试之敏感信息收集
  14. 模仿dos窗口下的windows窗口程序
  15. 怎么去除视频字幕清理视频字幕或水印的几种方法
  16. 【数据库】编写存储过程
  17. 《刻意练习》--读后感1
  18. 瑞萨RH850 FCL、FDL和EEL库的配置和使用
  19. 扬长避短,做自己最擅长的事情
  20. subprocess.Popen()

热门文章

  1. 小汤学编程之JAVA基础day01——JAVA基本概念、第一个JAVA程序
  2. 面试官:说说你知道多少种线程池拒绝策略
  3. VS Code编写html(2)
  4. 搜索引擎、相关性算法的测试
  5. 用命令导入导出MS SQL数据
  6. springboot中配置文件使用2
  7. 浏览器中的JavaScript
  8. COCI 2018/2019 CONTEST #2 Solution
  9. float,absolute脱离文档流的总结
  10. Python入门学习-DAY27- isinstance与issubclass、反射、内置方法