深入了解Java并发AQS的独占锁模式

深入了解Java并发AQS的独占锁模式

独占锁是Java并发编程中重要的一种锁机制,它可以保证共享资源同时只能被一个线程所访问和修改。AQS(AbstractQueuedSynchronizer)是Java中实现锁机制的基础,独占锁模式的实现也是基于AQS的ReentrantLock类。

AQS基本结构

AQS的核心是一个等待队列,其中包含了阻塞的线程,队列采用FIFO的顺序进行管理。其中还有一个同步状态值,它用于判断当前线程是否可能获得锁。

ReentrantLock实现独占锁

ReentrantLock是基于AQS实现独占锁的。它的核心代码如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();
        protected final boolean tryRelease(int releases) {
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            setState(0);
            setExclusiveOwnerThread(null);
            return true;
        }
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        protected final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

    static final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    }

    static final class FairSync extends Sync {
        final void lock() {
            acquire(1);
        }
    }

    public void lock() {
        sync.lock();
    }

    public void unlock() {
        sync.release(1);
    }
}

ReentrantLock中有一个Sync类,它是AQS的子类,定义了锁的基本操作方法。其中lock()方法用于获得锁,它首先尝试CAS修改同步状态值,如果成功,则将当前线程设为占有线程;否则调用acquire方法将该线程加入等待队列,实现阻塞并等待获取锁的功能。tryRelease方法用于释放锁,它首先判断当前线程是不是锁的持有者,如果不是,则抛出IllegalMonitorStateException异常,否则就清空同步状态,并将占有线程设为null。isHeldExclusively方法则用于判断当前线程是否占有锁,使用时一定注意,这个方法不是“当前线程是否等待锁”,而是“当前线程是否占有锁”。newCondition方法用于返回一个新的ConditionObject对象。

独占锁模式的示例

示例1:基于ReentrantLock实现简单的互斥

下面的示例演示了基于ReentrantLock实现简单的互斥的过程:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    private static Lock lock = new ReentrantLock();
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter++;
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();
            counter--;
            lock.unlock();
        }).start();

        Thread.sleep(3000);
        System.out.println(counter);
    }
}

在上面的代码中,定义了一个ReentrantLock对象,并定义了一个counter变量。在两个线程中分别对该变量进行了加一和减一的操作,并在执行完后释放锁。在最后打印counter的值,我们可以看到它的值保持不变,这是因为两个线程都无法同时对counter变量进行操作。

示例2:基于ReentrantLock实现带有等待时限的锁

下面的示例演示了一个带有等待时限的锁的实现方式:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                if (lock.tryLock(3, TimeUnit.SECONDS)) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("Thread A completed");
        }).start();

        new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
            System.out.println("Thread B completed");
        }).start();

        Thread.sleep(20000);
        System.out.println("Main thread completed");
    }
}

在上面的代码中,首先定义了一个ReentrantLock对象,然后两个线程分别尝试获取该锁。线程A在获取锁前使用tryLock方法进行了3秒钟的等待,超过此时间则终止等待,如果获取到锁则会进行5秒钟的睡眠操作。线程B则直接通过lock方法获取锁,并在获取到锁后进行10秒钟的睡眠操作。最后在主线程中等待了20秒钟后打印结果,我们可以看到线程A在3秒钟后成功获取锁,并完成了操作;线程B在10秒钟后也完成了操作。在两个线程完成操作后即可释放锁,其他线程就可以获取该锁了。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入了解Java并发AQS的独占锁模式 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • Java中多线程的ABA场景问题分析

    Java中多线程的ABA场景问题分析 ABA场景问题简介 多线程中,如果一个线程在读取一个共享变量时,另一个线程把它修改为另外一个值,再修改回原来的值,这时第一个线程可能会检查到期望的值,但是并没有发现这个值已经被修改过,这种情况就叫做ABA场景问题。 ABA场景问题如何解决 Java中提供了一个原子变量类AtomicStampedReference来解决A…

    多线程 2023年5月16日
    00
  • java线程并发控制同步工具CountDownLatch

    当多个线程并发执行时,可能会出现资源争抢、数据不一致等问题。因此,Java 提供了一些同步工具来帮助我们实现线程并发控制。其中,CountDownLatch 是一个非常实用的同步工具,它可以使线程等待其他线程执行完成再继续执行。 CountDownLatch 的概述 CountDownLatch 是 Java.util.concurrent 包下的一个同步工…

    多线程 2023年5月16日
    00
  • 批处理程序中的“多线程”处理代码

    我将为你详细讲解批处理程序中的“多线程”处理代码的完整攻略,希望能够帮助到你。 理解多线程 在批处理程序中实现“多线程”处理,首先需要明确多线程的概念。简单来说,多线程是指在一个程序中同时运行多个线程,每个线程都可以独立地执行不同的任务,从而将程序的处理能力提高到一个新的层次。 在批处理中,我们可以通过调用 START 命令创建新的线程,从而实现多线程处理任…

    多线程 2023年5月17日
    00
  • java之使用多线程代替for循环(解决主线程提前结束问题)

    下面是使用多线程代替for循环的攻略,我将分几个部分进行讲解。 什么是多线程? 多线程是指同时执行多个线程(程序),也就是并发执行。与单线程相比,多线程可以将程序的性能提高数倍,但是多线程也存在一些问题,如线程安全、线程同步等。 为什么要使用多线程代替for循环? 在Java中,使用for循环进行数据的处理是非常常见的操作。但是当待处理的数据量较大时,使用f…

    多线程 2023年5月17日
    00
  • 详解Java七大阻塞队列之SynchronousQueue

    详解Java七大阻塞队列之SynchronousQueue 简介 Java提供了七种不同类型的阻塞队列,SynchronousQueue是其中比较特殊的一种。它的特点是在插入元素时必须等待另外一个线程同时要移除这个元素,否则阻塞当前线程;同理,在移除元素时也必须等待另一个线程同时要插入这个元素,否则也会阻塞当前线程。这使得SynchronousQueue成为…

    多线程 2023年5月16日
    00
  • Java使用Thread和Runnable的线程实现方法比较

    Java使用Thread和Runnable的线程实现方法比较 Java中的线程实现主要有两种方式:使用Thread类或使用Runnable接口。这两种方法都可以用于实现多线程编程,但使用方式和应用场景不同。在本文中,我们将比较这两种方法之间的异同点,并提供示例说明。 Thread类实现多线程 Java中的Thread类是一种封装了操作系统线程的类,使用这个类…

    多线程 2023年5月16日
    00
  • C#多线程系列之多线程锁lock和Monitor

    C#多线程系列之多线程锁lock和Monitor 在多线程编程中,为了保证数据的安全性和正确性,需要使用到锁。本文主要介绍C#中的多线程锁lock和Monitor。 什么是锁? 锁是一种同步机制,可以确保多个线程在访问共享资源时不会产生冲突。在执行某个代码块时,只有获得了锁的线程才能执行,其他线程则需要等待锁的释放。这样可以保证同一时刻只有一个线程对共享资源…

    多线程 2023年5月16日
    00
  • redis-benchmark并发压力测试的问题解析

    那我来详细讲解一下“redis-benchmark并发压力测试的问题解析”的完整攻略。 什么是redis-benchmark并发压力测试? redis-benchmark是一个Redis自带的基准测试工具,可以通过运行redis-benchmark命令进行并发请求测试。该命令提供了多种测试模式、并发连接数、请求大小、数据类型和其他选项,可用于测试Redis服…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部