上次谈到为防止多个线程同时进入程序某一特定部分而导致资源竞争,会在同步方法执行前加上锁。除了synchronized关键字外,还有一个接口,Lock接口同样可以实现对同步方法进行加锁。
不同于synchronized关键字,使用Lock接口的实例是显式加锁,即需要手动加锁和解锁,同时,显示加锁给协调带来了更多的控制功能。
ReentrantLock
ReentrantLock是Lock的一个具体实例,用于创建相互排斥的锁。
public void method() {
//ReentrantLock()等价于ReentrantLock(false)即将锁给任意一个等待的线程
Lock lock = new ReentrantLock();
//上锁
lock.lock();
try {
for (i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
} finally {
//解锁
lock.unlock();
}
}
通常情况下,多个线程之间不仅仅需要相互排斥,还要相互通信。通过调用Lock对象的newCondition()方法而创建的对象来实现线程间通信。
public class newConditionText {
static int count = 0;
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new TextAdd());
executor.execute(new TextSub());
executor.shutdown();
}
static class TextAdd implements Runnable {
Account account = new Account();
@Override
public void run() {
while (true) {
account.add();
}
}
}
static class TextSub implements Runnable {
Account account = new Account();
@Override
public void run() {
while (true) {
account.sub();
}
}
}
static class Account {
private static Lock lock = new ReentrantLock();
private static Condition condition1 = lock.newCondition();
private static Condition condition2 = lock.newCondition();
public void add() {
lock.lock();
try {
//当count大于2时,线程等待
while (count > 2) {
condition1.await();
}
count++;
System.out.println("add:" + count);
//唤醒等待中的sub()
condition2.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
public void sub() {
lock.lock();
try {
//当count小于1时,线程等待
while (count < 1) {
condition2.await();
}
count--;
System.out.println("sub:" + count);
//唤醒等待中的add()
condition1.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
}
}