Java锁原理学习

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

原文地址:https://www.cnblogs.com/jieyuefeng/p/11974278.html

时间: 2024-08-27 09:03:38

Java锁原理学习的相关文章

有关Java 锁原理

锁 锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到目标的内容!对Java并发包,锁的实现基本在java.util.concurrent.locks包中,说“基本”是因为,在java.util.concurrent中还有CountDownLatch(可以看成带计数器的锁),CyclicBarrier,Semaphore(类似于信号量,但是也可以看成一

Java 锁的学习

个人学习整理,所有资料均来源于网络,非原创. 死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用.请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源.一个进程因请求资源而阻塞时,对已获得的资源保持不放.非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺.循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源. 不难看出

java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁

1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数封装的代码操作tickets,同步代码块中的锁我们可以指定.假设我们事先不知道同步函数用的是什么锁:如果在同步代码块中指定的某个锁(测试)和同步函数用的锁相同,就不会出现线程安全问题,如果锁不相同,就会发生线程安全问题. 看下面的代码:t1线程用的同步锁是obj,t2线程在操作同步函数的资源,假设不

java基础知识回顾之java Thread类学习(五)--java多线程安全问题(锁)同步的前提

这里举个例子讲解,同步synchronized在什么地方加,以及同步的前提: * 1.必须要有两个以上的线程,才需要同步. * 2.必须是多个线程使用同一个锁. * 3.必须保证同步中只能有一个线程在运行,锁加在哪一块代码 那么我们要思考的地方有:1.知道我们写的哪些是多线程代码 2.明确共享数据 3.明确多线程运行的代码中哪些语句是操作共享数据的.. 4.要确保使用同一个锁. 下面的代码:需求:两个存户分别往银行存钱,每次村100块,分三次存完. class bank{ private int

java基础知识回顾之java Thread类学习(四)--java多线程安全问题(锁)

上一节售票系统中我们发现,打印出了错票,0,-1,出现了多线程安全问题.我们分析为什么会发生多线程安全问题? 看下面线程的主要代码: @Override public void run() { // TODO Auto-generated method stub while(true){ if(ticket > 0){//当线程0被调起的时候,当执行到这条判断语句的时候,线程1被调起抢了CPU资源,线程0进入冻结状态. try { Thread.sleep(100);//中断当前活跃的线程,或者

学习JAVA的原理

了解JAVA的原理,才有可能真正体会JAVA的一切,学习任何事情,只要能掌握原理,就会比较顺利. 学习JAVA语言很简单,毕竟JAVA语言也只包含五十多个关键词(keyword)与几十个算符(operator),再加上JAVA语法(syntax)也很简单,所以一般人可以很快就学会JAVA语言.

关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在jdk java.util 并发包中已经为我们提供了这些方法去加锁, 比如synchronized 关键字 或者Lock 锁,都可以处理. 但是我们现在的应用程序如果只部署一台服务器,那并发量是很差的,如果同时有上万的请求那么很有可能造成服务器压力过大,而瘫痪. 想想双十一 和 三十晚上十点分支付宝红

Java架构师学习路线图,第6点尤为重要!

Web应用,最常见的研发语言是Java和PHP.后端服务,最常见的研发语言是Java和C/C++.大数据,最常见的研发语言是Java和Python. 可以说,Java是现阶段中国互联网公司中,覆盖度最广的研发语言,掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地. 有不少朋友问,除了掌握Java语法,还要系统学习哪些Java相关的技术,今天分享一个,互联网Java技术学习路线图. 一:常见模式与工具学习Java技术体系,设计模式,流行的框架与组件是必

Java并发指南开篇:Java并发编程学习大纲

Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容. 这里不仅仅是指使用简单的多线程编程,或者使用juc的某个类.当然这些都是并发编程的基本知识,除了使用这些工具以外,Java并发编程中涉及到的技术原理十分丰富.为了更好地把并发知识形成一个体系,也鉴于本人没有能力写出这类文章,于是参考几位并发编程专家的博客和书籍,做一个简单的整理. 一:并发基础和多线程 首先需要学习的就是并发的基础知识,什么是并发,为什么要并发,多线程的概念,线程安全的概念等. 然后学会使用Java中的Threa