java常用锁

本文详细探讨了Java中ReentrantLock的实现原理,包括乐观锁、悲观锁、公平锁及非公平锁的区别,同时对比了ReentrantLock与synchronized关键字,并深入分析了Semaphore信号量的工作机制。

java常用锁类型

常见的锁大致可以分为:乐观锁,悲观锁,排他锁,共享锁,分段锁,自选锁,公平锁,非公平锁等。。今天来学基于CAS非加锁实现的乐观锁

ReentrantLock锁

ReentrantLock类是一种可重入,公平/非公平,独占锁,它于synchronized具有相同的功能和语义,但是它更强大,它支持中断,超时等操作。Sync是ReentrantLock的内部类,他的两个子类分别代表公平锁和非公平锁,ReentrantLock可以在构造方法选择是否公平。

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

lock和unlock都是基于内部类Sync实现的,因此我们了解的重点是Sync

内部类Sync

Sync是ReentrantLock定义的一个静态内部类,他继承了AbstractQueuedSynchronizer类。它有两个实现类分别为NonfairSync(非公平锁),FairSync(公平锁),

非公平锁lock方法
        final void lock() {
            if (compareAndSetState(0, 1))//尝试获得锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
            	  调用AQS的acquire(int arg)方法
                acquire(1);
        }
公平锁lock方法
        final void lock() {
            acquire(1);//直接调用AQS的acquire(int arg)方法
        }

其实无论是公平或者非公平锁的lock方法最重要的就是tryAcquire(int acquires)方法

非公平锁tryAcquire(int acquires)方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
        
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();//获得当前线程引用
            int c = getState();获得状态
            if (c == 0) {
            	  //尝试设置状态
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前线程为持锁线程则重入累加
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
公平锁tryAcquire(int acquires)方法
 protected final boolean tryAcquire(int acquires) {
 		  //公平锁相对于非公平锁就是多了hasQueuedPredecessors()来实现公平策略
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
hasQueuedPredecessors()
    public final boolean hasQueuedPredecessors() {

        Node t = tail;//获得尾部节点
        Node h = head;//获得前继节点
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

首先头部节点不等于尾部节点则代表队列不为空--------->true
s为空则代表有头节点----------->true
s不为空且s的线程不等于当前线程--------->true

demo
public class TestReentrantLock {
    private static volatile ReentrantLock lock=new ReentrantLock();
    public static void main(String[] args) {
        new Thread(()->{
            lock.lock();
            System.out.println("线程一先工作呢");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程一下班啦");
            lock.unlock();
        }).start();
        new Thread(()->{
            lock.lock();
            System.out.println("线程二在工作呢");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程二下班啦");

            lock.unlock();
        }).start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMJ2UeYq-1592028252772)(/images/pasted-16.png)]

Semaphore信号量

Semaphore是一种公平/非公平锁,共享锁。它可以做为一个计数器,用来保护一个或多个锁。跟上面讲解的ReentrantLock独占锁一样,核心代码都是基于类中定义的静态内部类Sync。

acquire()方法
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);//可以被中断的获锁方法
    }

从信号量获取一个许可,如果无可用许可前将一直阻塞等待。

acquireSharedInterruptibly(int arg)方法
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())//如果当前线程被中断就抛出异常
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//尝试获得异常
            doAcquireSharedInterruptibly(arg);//获锁失败放进等待队列
    }
tryAcquireShared(int arg)方法
非公平锁实现
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
        
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();//获得可用锁数量
                int remaining = available - acquires;
                //如果没有锁了就直接返回否则尝试获得锁资源
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
公平锁实现
protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())//公平策略就是判断当前节点是否有前驱节点(跟上面的ReentrantLock的公平策略一样的)
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
doAcquireSharedInterruptibly(int arg)

分析请见

release(int arg)方法
    public void release() {
        sync.releaseShared(1);
    }

释放信号量中的一个许可,如果多次调用会扩大信号量的可用数量。

releaseShared(int arg)方法
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg))//尝试释放锁 {
            doReleaseShared();
            return true;
        }
        return false;
    }
tryReleaseShared(int releases)方法
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();//获得队列中可用锁数量
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))//尝试设置数量
                    return true;
            }
        }
doReleaseShared()方法

分析请见

### Java 常用类型及其实现方式 #### 1. **Synchronized ** `synchronized` 是一种内置机制,用于解决多线程环境下的同步问题。它是通过 JVM 实现的隐式,适用于简单的场景。 - **作用范围**: 可以应用于方法级别或者代码块级别。 - **实现方式**: - 方法级别的 `synchronized`: 将整个方法标记为同步方法。 ```java public synchronized void method() { // 同步代码块 } ``` - 代码块级别的 `synchronized`: 对特定的对象加。 ```java Object lock = new Object(); public void method() { synchronized(lock) { // 同步代码块 } } ``` 这种的优点在于其简单性和自动管理特性[^2]。当线程离开同步区域时,无论正常退出还是抛出异常,都会自动释放。 --- #### 2. **ReentrantLock** `ReentrantLock` 提供了更灵活的功能,允许显式地获取和释放。相比 `synchronized`,它的功能更加丰富。 - **特点**: - 支持公平和非公平的选择。 - 提供可中断的等待能力。 - 提供超时获取的能力。 - **实现方式**: ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Lock lock = new ReentrantLock(); public void method() { lock.lock(); // 显式获取 try { // 执行临界区代码 } finally { lock.unlock(); // 显式释放 } } ``` 这种方式适合需要更多控制权的情况,例如处理复杂的逻辑或需要更高的性能优化。 --- #### 3. **读写 (ReadWriteLock)** `ReadWriteLock` 接口提供了分离的读和写,允许多个线程同时读取共享资源而互不干扰,但在有写操作时会阻止其他任何线程访问。 - **特点**: - 多个线程可以同时持有读。 - 如果某个线程持有写,则不允许其他线程持有任何形式的。 - **实现方式**: ```java import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; ReadWriteLock rwLock = new ReentrantReadWriteLock(); public void readMethod() { rwLock.readLock().lock(); // 获取读 try { // 执行只读操作 } finally { rwLock.readLock().unlock(); // 释放读 } } public void writeMethod() { rwLock.writeLock().lock(); // 获取写 try { // 执行修改操作 } finally { rwLock.writeLock().unlock(); // 释放写 } } ``` 这种方法特别适合高并发读操作的应用场景。 --- #### 4. **偏向与轻量级** 这两种属于 JVM 层面的优化策略,通常不需要开发者手动干预。 - **偏向**: - 当一个线程第一次获取时,JVM 会将该标记为偏向于这个线程的状态。 - 如果后续只有同一个线程再次请求此,则无需执行真正的定操作。 - **轻量级**: - 在多个线程竞争同一把的情况下,如果发现当前已经被占用且不是偏向自己,则升级为轻量级。 - 轻量级利用 CAS 操作尝试原子化地更新标志位,减少重量级带来的开销[^4]。 这些机制由 JVM 自动完成切换,主要用于提高低竞争情况下的性能表现。 --- #### 5. **TryLock 和非阻塞** `tryLock()` 方法提供了一种非阻塞的方式来尝试获取。如果无法立即获得,则直接返回而不挂起线程。 - **特点**: - 不会造成线程阻塞,提高了系统的响应速度。 - 特别适合那些不能容忍长时间等待的任务。 - **实现方式**: ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Lock lock = new ReentrantLock(); public boolean attemptToAcquireLock() { if (lock.tryLock()) { // 尝试获取 try { // 成功获取后的业务逻辑 return true; } finally { lock.unlock(); // 确保最终解 } } else { // 返回失败结果 return false; } } ``` 这种方式非常适合实时性要求较高的应用场景[^3]。 --- ### 总结 Java 中常见的机制包括但不限于 `synchronized`, `ReentrantLock`, `ReadWriteLock`, 偏向以及轻量级等。每种都有各自的特点和适用场景,在实际开发过程中应根据具体需求选择合适的方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值