深入理解Java并发锁

图片

在Java中,并发锁是用来控制多个线程对共享资源的访问,确保数据的一致性和完整性。Java提供了多种并发锁机制,包括内置锁(synchronized)、显示锁(如ReentrantLock)、原子变量、并发容器以及一些高级技巧如乐观锁和String.intern()等。

1. synchronized关键字

特点:

synchronized是Java内建的锁机制,它提供了隐式锁,也称为内部锁或监视器锁。

当一个线程获得对象的锁后,其他试图获取该锁的线程将会被阻塞,直到锁被释放。

实现原理:

每个对象都有一个内置锁和一个计数器。当线程请求锁时,JVM将计数器加一。

如果线程已经持有锁,计数器会再次增加,这允许同一个线程多次同步。

当线程完成同步代码块时,计数器减一。当计数器为零时,锁被释放。

使用场景:

适用于方法或代码块的简单同步。

优点:

简单易用,不需要手动释放锁。

缺点:

不能被中断;不支持公平性;无法设置超时。

代码示例:


public class SynchronizedExample {
    private Object lock = new Object();

    public void method() {
        synchronized (lock) {
            // 临界区代码
        }
    }
}

2. ReentrantLock

特点:

ReentrantLock是一个可重入互斥锁,由java.util.concurrent.locks包提供。

支持公平锁和非公平锁。

实现原理:

基于AbstractQueuedSynchronizer(AQS)框架实现。

维护了一个状态变量来跟踪锁的状态。

使用场景:

适用于需要高度自定义和灵活的同步控制的场景。

优点:

高度灵活,支持中断、超时、公平性。

缺点:

必须手动释放锁,否则可能导致死锁。

代码示例:


import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void method() {
        lock.lock();
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
    }
}

图片

3. ReadWriteLock

特点:

允许多个读线程同时访问,但只允许一个写线程。

读写锁通常用于读多写少的场合。

实现原理:

通过两个锁来实现:一个读锁和一个写锁。

读锁是共享的,写锁是独占的。

使用场景:

适用于读多写少的数据结构,如缓存系统。

优点:

提高并发性能,减少锁竞争。

缺点:

写操作可能会饥饿,如果读操作持续不断。

代码示例:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void read() {
        rwLock.readLock().lock();
        try {
            // 读操作代码
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public void write() {
        rwLock.writeLock().lock();
        try {
            // 写操作代码
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

4. StampedLock

特点:

StampedLock支持乐观读、悲观读、写锁和锁的升级。

它提供了一个版本号来避免不必要的唤醒。

实现原理:

使用了一种称为“乐观读”的技术,通过标记来避免长时间的等待。

使用CAS操作来尝试获取和释放锁。

使用场景:

适用于高并发且读多写少的场景。

优点:

高性能,特别是在高并发环境下。

缺点:

相对复杂,需要更小心地管理状态。

代码示例:

import java.util.concurrent.locks.StampedLock;

public class StampedLockExample {
    private final StampedLock stampedLock = new StampedLock();

    public void method() {
        long stamp = stampedLock.writeLock();
        try {
            // 临界区代码
        } finally {
            stampedLock.unlockWrite(stamp);
        }
    }
}

5. Semaphore、CountDownLatch和CyclicBarrier

这三个工具类虽然不是锁,但它们常用于多线程的同步控制。

Semaphore: 控制同时访问特定资源的线程数量。适用于限制并发线程数。

CountDownLatch: 允许一个或多个线程等待其他线程完成操作。适用于等待一组线程完成任务后再执行的场景。

CyclicBarrier: 允许一组线程相互等待,直到所有线程都准备好再同时执行。适用于多线程计算数据的场景。

总结

Java提供了丰富的并发锁和同步工具,以满足不同的并发需求。从简单的synchronized到复杂的StampedLock,每种锁都有其适用场景和特定的优缺点。了解这些并发工具的原理和使用方式对于编写高效且线程安全的Java程序至关重要。在实际开发中,选择合适的锁取决于具体的需求、性能考虑以及代码的复杂性。

图片

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值