Java中的锁是一种同步机制,用于控制多个线程对共享资源的访问,避免数据竞争造成的错误。Java提供了多种锁,下面将介绍Java中五种常用的锁及其应用场景。

一、synchronized锁

synchronized是Java中最基本的一种锁。它是通过对方法或代码块加锁的方式,实现对共享资源的访问控制。

使用synchronized锁时,需要注意以下几点:

  1. synchronized锁只能保证单个线程对共享资源的访问,不能保证在多线程条件下的访问。
  2. 使用synchronized锁时,必须在同一线程内去获取锁和释放锁,否则会引起死锁。
  3. synchronized锁在性能上有影响,因为需要进行锁的获取和释放操作,会引起线程上下文切换导致性能下降。

以下是一个使用synchronized锁的示例代码:

public class SynchronizedLockDemo {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    public synchronized int getCount() {
        return count;
    }
}

二、ReentrantLock锁

ReentrantLock是一个可重入锁,它可以更灵活地控制共享资源的访问。与synchronized锁不同的是,ReentrantLock可以在不同的线程中进行获取和释放锁的操作。

使用ReentrantLock锁时,需要注意以下几点:

  1. 使用ReentrantLock必须先获取锁再进行操作,操作完成后需要释放锁,否则会引起死锁。
  2. 使用ReentrantLock时需要保证加锁和解锁是在try-finally代码块中,防止出现异常时无法释放锁的情况。
  3. ReentrantLock比synchronized锁更灵活,可以根据需要进行公平或非公平的锁的分配。

以下是一个使用ReentrantLock锁的示例代码:

public class ReentrantLockDemo {
    private ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

三、ReadWriteLock锁

ReadWriteLock锁是一种特殊的锁,它支持对共享资源的读写分离。ReadWriteLock提供了读锁和写锁两种不同的锁,读锁用于支持多个线程同时读取共享资源,写锁用于控制对共享资源的独占访问。

使用ReadWriteLock锁时,需要注意以下几点:

  1. 当存在多个线程同时读取同一共享资源时,可以使用读锁进行访问,这可以提高并发效率。
  2. 当需要写入共享资源时,需要获取写锁。
  3. 读锁可以共享,写锁是互斥的。

以下是一个使用ReadWriteLock锁的示例代码:

public class ReadWriteLockDemo {
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private int count = 0;
    public void increment() {
        lock.writeLock().lock();
        try {
            count++;
        } finally {
            lock.writeLock().unlock();
        }
    }
    public int getCount() {
        lock.readLock().lock();
        try {
            return count;
        } finally {
            lock.readLock().unlock();
        }
    }
}

四、StampedLock锁

StampedLock锁是一种乐观锁,它可以实现比ReadWriteLock更高效的读写分离。StampedLock锁的思想是在读的过程中也允许写入操作,只要写入时不会改变数据结构的完整性即可。

使用StampedLock锁时,需要注意以下几点:

  1. 使用StampedLock锁的时候,要求共享资源必须是基于内部对象实现的。
  2. StampedLock锁提供了读锁、写锁和乐观读锁三种不同的锁。
  3. 使用StampedLock锁时需要调用tryOptimisticRead方法获取乐观读锁,并且每次获取乐观读锁之后需要验证版本号是否发生变化,如果发生变化需要重新获取读锁。

以下是一个使用StampedLock锁的示例代码:

public class StampedLockDemo {
    private StampedLock lock = new StampedLock();
    private int count = 0;
    public void increment() {
        long stamp = lock.writeLock();
        try {
            count++;
        } finally {
            lock.unlockWrite(stamp);
        }
    }
    public int getCount() {
        long stamp = lock.tryOptimisticRead();
        int c = count;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                c = count;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return c;
    }
}

五、Condition锁

Condition锁是Java中一种高级锁,它可以让线程在等待某个条件成立时,自动地释放锁并进入等待状态,等待条件成立后自动唤醒线程重新获取锁。

使用Condition锁时,需要注意以下几点:

  1. 使用Condition锁时需要先调用Lock().newCondition()方法获取Condition对象。
  2. 使用Condition锁需要在获取锁的前提下进行,否则会抛IllegalMonitorStateException异常。

以下是一个使用Condition锁的示例代码:

public class ConditionDemo {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private int count = 0;
    public void increment() {
        lock.lock();
        try {
            while (count == 10) {
                condition.await();
            }
            count++;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public int getCount() {
        lock.lock();
        try {
            while (count == 0) {
                condition.await();
            }
            int c = count--;
            condition.signalAll();
            return c;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return -1;
        } finally {
            lock.unlock();
        }
    }
}

以上是Java中常用的五种锁及其应用场景的简介,使用不同的锁可以根据实际业务需求进行选择,提高程序的并发性能。在使用锁的过程中,需要根据实际情况选择最适合的锁和锁的级别,避免出现死锁等问题。