Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • MacOS
  • Linux
  • Windows
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • MacOS
  • Linux
  • Windows
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
  • Java集合

    • Java集合概述
    • ArrayList
    • LinkedList
    • HashMap
    • LinkedHashMap
    • HashSet
    • TreeMap
    • Queue&Deque
  • Java并发

    • Java并发概述
    • 线程与进程
    • Thread类与线程生命周期
    • 线程安全
    • synchronized关键字
    • volatile关键字
    • Java内存模型(JMM)
    • 线程间通信
    • 线程池
    • 并发工具类
    • 原子操作类Atomic
    • 并发锁
      • 1. ReentrantLock(可重入锁)
      • 2. ReadWriteLock(读写锁)
      • 3. StampedLock
      • 4. Semaphore(信号量)
      • 5. Lock 与 Synchronized 的比较
      • 6. 总结
    • 并发容器
    • ConcurrentHashMap
    • BlockingQueue
    • CopyOnWriteArrayList
    • ThreadLocal
    • Fork/Join框架
    • ScheduledThreadPoolExecutor
    • CompletableFuture
  • Java IO

    • Java IO概述
  • JVM

    • JVM概述
  • Java
  • Java并发
Wray
2024-10-31
目录

并发锁

并发锁是 Java 并发编程中用于解决多线程竞争资源的主要机制之一。锁的作用是确保在同一时间只有一个线程能够访问共享资源,从而避免数据不一致的问题。Java 提供了多种类型的锁来应对不同的并发场景。

# 1. ReentrantLock(可重入锁)

  • 概念:ReentrantLock 是 Java 提供的一种显式锁,相比于 synchronized,它提供了更多的锁控制能力,如尝试锁、定时锁、可中断锁等。
  • 特性:
    • 可重入性:ReentrantLock 是可重入的,意味着同一个线程可以多次获取同一个锁而不会发生死锁。
    • 公平性:可以通过构造方法选择公平锁和非公平锁。公平锁按请求顺序获取锁,非公平锁可以提高吞吐量,但可能会导致线程饥饿。
    • 灵活性:可以使用 tryLock() 尝试获取锁,lockInterruptibly() 响应中断,提供了比 synchronized 更灵活的锁控制。

示例代码:

ReentrantLock lock = new ReentrantLock();

Runnable worker = () -> {
    try {
        if (lock.tryLock(1000, TimeUnit.MILLISECONDS)) {
            try {
                System.out.println(Thread.currentThread().getName() + " acquired the lock.");
                Thread.sleep(2000);
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " released the lock.");
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " could not acquire the lock.");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};

for (int i = 0; i < 3; i++) {
    new Thread(worker).start();
}

在这个例子中,线程尝试获取锁,如果成功则执行相应操作,最后释放锁。

# 2. ReadWriteLock(读写锁)

  • 概念:ReadWriteLock 是一种特殊类型的锁,用于提高并发性。它包含两个锁——一个读锁和一个写锁。
  • 特性:
    • 读锁共享:多个线程可以同时获取读锁,因此允许多个读操作并行进行。
    • 写锁独占:写锁是独占的,只有当没有线程持有读锁或写锁时,线程才能获取写锁。
  • 应用场景:适用于读多写少的场景,可以大幅提高并发性能。

示例代码:

ReadWriteLock rwLock = new ReentrantReadWriteLock();

Runnable readTask = () -> {
    rwLock.readLock().lock();
    try {
        System.out.println(Thread.currentThread().getName() + " is reading.");
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        rwLock.readLock().unlock();
    }
};

Runnable writeTask = () -> {
    rwLock.writeLock().lock();
    try {
        System.out.println(Thread.currentThread().getName() + " is writing.");
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        rwLock.writeLock().unlock();
    }
};

new Thread(readTask).start();
new Thread(readTask).start();
new Thread(writeTask).start();

在这个例子中,多个线程可以同时获取读锁,但写锁需要独占访问。

# 3. StampedLock

  • 概念:StampedLock 是 Java 8 引入的一种改进版的读写锁,提供了乐观读锁以进一步提升并发性能。
  • 特性:
    • 乐观读锁:StampedLock 提供了一种乐观读锁,允许读线程在不阻塞写操作的情况下读取数据,从而提高性能。
    • 悲观读锁和写锁:也支持传统的悲观读锁和写锁,与 ReadWriteLock 类似。
  • 使用场景:适用于读操作频繁,但写操作较少的场景,可以提高并发读的性能。

示例代码:

StampedLock stampedLock = new StampedLock();

Runnable readTask = () -> {
    long stamp = stampedLock.tryOptimisticRead();
    try {
        System.out.println(Thread.currentThread().getName() + " is trying optimistic read.");
        Thread.sleep(1000);
        if (!stampedLock.validate(stamp)) {
            System.out.println(Thread.currentThread().getName() + " optimistic read failed, acquiring read lock.");
            stamp = stampedLock.readLock();
        }
        System.out.println(Thread.currentThread().getName() + " read completed.");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        stampedLock.unlockRead(stamp);
    }
};

new Thread(readTask).start();
new Thread(readTask).start();

在这个例子中,线程尝试乐观读取数据,如果在读取期间数据被修改,则会升级为悲观读锁。

# 4. Semaphore(信号量)

  • 概念:Semaphore 用于控制同时访问某一特定资源的线程数量,通过许可证机制来实现对资源的并发控制。
  • 应用场景:适用于限制对有限资源的访问,例如限制同时访问数据库连接的线程数。

示例代码:

Semaphore semaphore = new Semaphore(2);

Runnable worker = () -> {
    try {
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName() + " acquired a permit.");
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        semaphore.release();
        System.out.println(Thread.currentThread().getName() + " released a permit.");
    }
};

for (int i = 0; i < 5; i++) {
    new Thread(worker).start();
}

在这个例子中,最多允许两个线程同时执行任务,其他线程会等待许可证的释放。

# 5. Lock 与 Synchronized 的比较

  • 显式与隐式:synchronized 是隐式的,不需要手动释放锁,Lock 需要显式地获取和释放锁。
  • 功能:Lock 提供了更多的功能,如可中断锁、定时锁、公平锁等,而 synchronized 的功能相对简单。
  • 性能:在某些场景下,Lock 比 synchronized 性能更好,特别是在竞争激烈时。

# 6. 总结

并发锁是 Java 并发编程中的重要工具,通过合理使用 ReentrantLock、ReadWriteLock、StampedLock 和 Semaphore,可以有效地解决线程竞争问题,确保共享资源的安全访问。每种锁都有其适用的场景,开发者需要根据具体的需求选择合适的锁,以达到最佳的并发性能和线程安全。理解并发锁的使用和原理,有助于在高并发场景中编写更加健壮的代码。

上次更新: 2024/11/01, 13:45:14
原子操作类Atomic
并发容器

← 原子操作类Atomic 并发容器→

Copyright © 2023-2024 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式