Lock与Condition

538 阅读3分钟

Lock与Condition

LockCondition是Java在1.5中引入的,

Lock用来解决同步的问题, Condition用于解决协同的问题,

Lock对应synchronized, Condition对应wait() / nofity(),

Lock/Condition与synchronized/wait/notify机制的差别

synchronized可以做不到, lock可以做到的事情

  1. synchronized锁获取锁的条件只有一个, 那就是对同一个对象资源进行锁竞争,无法拓展一些条件来判定是否可以获取锁。

  2. synchronized没有办法实现读写分离, 当有多个线程进行一个资源的获取时,可能只有一个线程是写,其他的线程只是想读, 但是synchronized不关心是读还是写,只要有多个线程来访问同一个资源,必须串行化等待。

  3. synchronized无法做到判断一个锁是否被占有,例如一个线程尝试获取一个资源时,如果这个资源被占有,这个线程就去做别的事情,如果没有被占有,则获取资源,synchronized当一个线程尝试获取被占有资源时,只能将其置为等待。

  4. synchronized无法实现公平锁,抢占锁时,很有可能后来的线程比先来的线程先行拿到锁,这样可能会造成一些线程一直在等待, 俗称线程饥饿

  5. synchronized无法在一个锁方法内通过条件判断释放当前的锁,只能等待当前方法完全执行结束。

lock无法做到的事情

synchronized可以在方法执行完毕或抛出异常后自动释放锁,而Lock则需要自己手动解锁,尤其是解锁操作为了安全一定要放在finally中, 注意Lock加锁和解锁必须成对出现,否则将造成锁无法释放, 其他的线程无法获取到锁

    public synchronized static void syncHello() {
        System.out.println("hello");
    }

    public static void lockHello() {
        lock.lock();
        try {
            System.out.println("hello");
        } finally {
            lock.unlock();
        }
    }

两者都可以做到的事情

两者都可以做到可重入锁。

synchronized的设定之一就是可重入锁,所谓可重入锁的意思就是一个线程持有了一个锁之后,在执行的过程当中,

又碰到了这个锁,那这时候就可以继续进入这个锁,不会因为这个锁资源已经被自己持有着,就不能再次进入,这个符合常规的思维逻辑,因为这个东西本身就在咱自己手上,咱当然可以一直使用。

synchronized实现的重入锁:

    public static void main(String[] args) {
        a();
        b();
    }

    public synchronized static void a() {
        System.out.println("a");
    }
    
    public synchronized static void b() {
        System.out.println("b");
    }

Lock实现的可重入锁,注意此处使用的是Lock的实现类ReentrantLock, 因为Lock自身是一个接口,有很多的实现类,

ReentrantLockLock下的子类之一,翻译成中文就是可重入的锁, 日常使用过程中,可重入锁是最常用到的,所以该类是使用最频繁的类。

    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        a();
        b();
    }

    public static void a() {
        lock.lock();
        System.out.println("a");
        lock.unlock();
    }

    public static void b() {
        lock.lock();
        System.out.println("b");
        lock.unlock();
    }

Condition

lock用于线程的上锁与解锁,而Condition则是用于线程的协同,例如何时进行等待,何时进行唤醒,常用的方法有:

  1. await,当前线程进入等待状态,并将锁让给其他人
  2. signal,通知一个await的线程可以继续执行
  3. signalAll,通知所有在当前lock锁中await的线程可以进行执行,但被唤醒的多个线程仍然要再次尝试获取到锁后才可以执行(意思是唤醒了多个线程后,为了保证线程安全,只有一个线程能够h获取到锁继续执行下面的代码)

用这个类主要是可以决定线程什么时候该停下来,什么时候开始做事,下面我们利用lockcondition来实现一个生产者 + 消费者模型。