锁、重入锁与条件

Java中的锁框架指的是java.util.concurrent.locks这个包里的,不同于对象的内置加锁同步以及java.lang.Object的等待/通知机制,包含锁框架的并发工具通过轮询锁、限时等待及其他方式改善了这种机制。

这里的锁指的是接口Lock

方法

包含了如下几种方法:

  • void lock():获取锁。当锁不可用时,调用线程会被强制一直等待直到锁可用。

  • void lockInterruptibly():除非调用线程被中断,否则获得锁。当锁不可用时,调用线程被强制一直等待,直到锁可用或线程中断。

  • Condition newCondition():返回一个新的绑定到当前锁示例之上的Condition实例。当Lock的实现类不支持条件时,抛出异常。

  • boolean tryLock():在该方法被调用时,锁可用则获得锁,当获得锁之后,该方法返回true;反之返回false。

  • boolean tryLock(long time, Time unit):当在制定等待时间内可用锁可用并且调用线程没被中断则获得锁,等待时间以java.util.concurrent.TimeUnit计(秒、毫秒等等)。当锁不可用时,调用线程会在指定时间内一直等待,直到锁可用或线程中断。中断抛出异常。

  • void unlock():释放锁

使用方式

一般使用锁我们需要用到下面这样的形式:

Lock  lock = ...;// 实例化接口
        lock.lock();
        try {
            //使用锁获取到的资源
        } catch (Exception e) {
            //异常处理
        } finally {
            lock.unlock();
        }

重入锁

类ReentranLock实现了接口Lock,这是一个可重入的互斥锁,这个锁是和一个持有量相关联的。当一条线程持有这个锁并且调用lock()、lockUnitinterruptibly()或者任意一个tryLock()方法重新获取锁,这个持有量就递增1。当线程调用unlock()方法,持有量就递减1。当持有量为0时,锁就会被释放。

方法

ReentranLock中的方法其实就是实现了Lock接口中的方法,这里需要说的是它的两个构造方法:

  • ReentrantLock():创建一个ReentrantLock的实例,该构造函数等价于ReentrantLock(false)。

  • ReentrantLock(boolen fair):创建一个拥有指定公平策略的实例。当这个锁需要使用公平的 排序策略 时,给fair复制为true。 在争用的环境下,这个锁倾向于将访问权限分配给 等待最久的线程

还有两个ReentranLock自己的方法:

  • boolean isFair():返回是否有公平策略。

  • boolean isHeldByCurrentThread():返回锁是否被当前线程持有。

注意,ReentranLock如果在没有持有锁的情况下调用unlock()会抛出IllegalMonitorStateException异常。

使用方法

示范如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class RLDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        final ReentrantLock lock = new ReentrantLock();
        class Worker implements Runnable {

            private final String name;

            Worker(String name) {
                this.name = name;
            }

            @Override
            public void run() {
                lock.lock();
                try {
                    if (lock.isHeldByCurrentThread()) {
                        System.out.printf("Thread %s entered critical section.%n", name);
                    }
                    System.out.printf("Thread %s performing work.%n", name);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                    System.out.printf("Thread %s finish work.%n", name);
                }finally {
                    lock.unlock();
                }
            }
        }

        executorService.execute(new Worker("ThreadA"));
        executorService.execute(new Worker("ThreadB"));
        try {
            executorService.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdownNow();
    }
}

结果为:

.PNG

可以看到,两条线程并不是交替执行的,当一个线程调用lock()方法而锁又不可用时,这个线程就一直禁用,直到锁可用(即锁被持有锁的进程释放)。

条件

接口Condition把Object的wait和notification方法(wait(),notify()和notifyAll())分解到不同的条件对象中。通过把这些条件和任意Lock实现的使用组合起来,起到让每个对象具有多重等待集合的作用。这里Lock取代了同步方法、代码块,Condition取代了Object的wait、notification方法。

一个Condition的实例原本就会绑定到一个锁上。可以使用Lock的newCondition()方法去获取一个针对特定Lock实例的Condition实例。

方法

Condition声明了下列方法:

  • void await():在接收到信号或者被中断之前,强制调用线程一直等待。

  • boolean await(long time, TimeUnit unit):在接收到信号、被中断,或者超过指定的等待时间之前,强制调用线程一直等待。

  • long awaitNanos(long nanosTimeout):同上。返回值为等待结果时间的估算

  • void awaitUninterruptibly():在接收到信号之前,强制当前线程一直等待。

  • boolean awaitUntil(Date deadline):同上上上。

  • void signal():唤醒一个等待中的线程。

  • void signalAll():唤醒所有等待中的线程。

使用方法

  1. 共享变量
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Shared {
    private char c;
    private volatile boolean available;
    private final Lock lock;
    private final Condition condition;
    Shared() {
        available = false;
        lock = new ReentrantLock();
        condition = lock.newCondition();
    }

    Lock getLock() {
        return lock;
    }

    char getSharedChar() {
        lock.lock();
        try {
            while (!available) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            available = false;
            condition.signal();
        } finally {
            lock.unlock();
            return c;
        }
    }

    void setSharedChar(char c) {
        lock.lock();
        try {
            while (available) {
                try {
                    condition.await();
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
            this.c = c;
            available = true;
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
  1. 生产者
import java.util.concurrent.locks.Lock;

public class Producer extends Thread{
    private final Lock lock;
    private final Shared shard;
    Producer(Shared shard) {
        this.shard = shard;
        this.lock = shard.getLock();
    }

    @Override
    public void run() {
        for (char ch = 'A'; ch <= 'D'; ch++) {
            lock.lock();
            shard.setSharedChar(ch);
            System.out.println(ch + " produced by producer");
            lock.unlock();
        }
    }
}
  1. 消费者
import java.util.concurrent.locks.Lock;

public class Consumer extends Thread {
    private final Lock lock;
    private final Shared shard;

    Consumer(Shared shard) {
        this.shard = shard;
        lock = shard.getLock();
    }

    @Override
    public void run() {
        char c;
        do {
            lock.lock();
            c = shard.getSharedChar();
            System.out.println(c + " consumed by consumer.");
            lock.unlock();
        } while (c != 'D');
    }
}
  1. 调用
public class PC {
    public static void main(String[] args) {
        Shared shared = new Shared();
        new Producer(shared).start();
        new Consumer(shared).start();
    }
}

结果如下:

2.PNG

可以看到,生产者和消费者获取到的锁均为共享变量当中的,而共享变量内部则使用条件变量来控制

猜你喜欢

转载自blog.csdn.net/qq_33487412/article/details/80867330