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();
}
}
结果为:
可以看到,两条线程并不是交替执行的,当一个线程调用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():唤醒所有等待中的线程。
使用方法
- 共享变量
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();
}
}
}
- 生产者
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();
}
}
}
- 消费者
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');
}
}
- 调用
public class PC {
public static void main(String[] args) {
Shared shared = new Shared();
new Producer(shared).start();
new Consumer(shared).start();
}
}
结果如下:
可以看到,生产者和消费者获取到的锁均为共享变量当中的,而共享变量内部则使用条件变量来控制