章
目
录
synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,之前我们讲过《Java多线程:线程间通信(等待/通知机制)》,ReentrantLock类也可以借助于Condition接口与newCondition()方法。
synchronized关键字在使用notify/notifyAll()方法进行通知时,被通知的线程是由JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知”。
synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在该一个实例上。
如果执行notifyAll()方法的话就会通知所有处于等待状态的线程,这样会造成很大的效率问题,而Condition可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。从而可以有选择性的进行线程通知,在调度线程上更加灵活。
Condition接口
//使当前线程在接到信号或被中断之前一直处于等待状态。
void await();
//使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit);
//使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout);
//使当前线程在接到信号之前一直处于等待状态。
void awaitUninterruptibly();
//使当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
boolean awaitUntil(Date deadline);
//唤醒一个等待线程。
void signal();
//唤醒所有等待线程。
void signalAll();
Condition实现等待/通知机制
当调用 await() 语句后,线程将被阻塞,必须执行完signal()所在的try语句块之后才释放锁,condition.await()后的语句才能被执行。
package ReentrantLockTest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
MyThread a = new MyThread(service);
a.start();
Thread.sleep(3000);
service.signal();
}
static public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
lock.lock();
try {
System.out.println("准备调用condition.await()方法,将该线程阻塞");
condition.await();
System.out.println("已调用condition.await()方法,此时已被 signal() 方法唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
try {
System.out.println("准备调用condition.signal()方法");
condition.signal();
Thread.sleep(3000);
System.out.println("已调用condition.signal()方法,去唤醒 await() 方法");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static public class MyThread extends Thread {
private MyService service;
public MyThread(MyService service) {
this.service = service;
}
@Override
public void run() {
service.await();
}
}
}
输出如下:
准备调用condition.await()方法,将该程序阻塞
准备调用condition.signal()方法
已调用condition.signal()方法,去唤醒 await() 方法
已调用condition.await()方法,此时已被 signal() 方法唤醒
多个Condition实例实现等待/通知机制
一个Lock对象中可以创建多个Condition实例,调用某个实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。
package ReentrantLockTest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
lock.lock();
try {
System.out.println("准备调用conditionA.await()方法,将该线程阻塞");
conditionA.await();
System.out.println(" awaitA 已被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
lock.lock();
try {
System.out.println("准备调用conditionB.await()方法,将该线程阻塞");
conditionB.await();
System.out.println(" awaitB 已被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalA() {
lock.lock();
try {
System.out.println("准备唤醒 conditionA 下的所有线程");
conditionA.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalB() {
lock.lock();
try {
System.out.println("准备唤醒 conditionB 下的所有线程");
conditionB.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package ReentrantLockTest;
public class Test {
public static void main(String[] args) throws InterruptedException {
LockTest lockTest = new LockTest();
ThreadA a = new ThreadA(lockTest);
a.setName("A");
a.start();
ThreadB b = new ThreadB(lockTest);
b.setName("B");
b.start();
Thread.sleep(3000);
lockTest.signalA();
}
static public class ThreadA extends Thread {
private LockTest lockTest;
public ThreadA(LockTest lockTest) {
this.lockTest = lockTest;
}
@Override
public void run() {
lockTest.awaitA();
}
}
static public class ThreadB extends Thread {
private LockTest lockTest;
public ThreadB(LockTest lockTest) {
this.lockTest = lockTest;
}
@Override
public void run() {
lockTest.awaitB();
}
}
}
程序输出:
准备调用conditionA.await()方法,将该线程阻塞
准备调用conditionB.await()方法,将该线程阻塞
准备唤醒 conditionA 下的所有线程
awaitA 已被唤醒
总结
在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。Condition的强大之处在于,对于一个锁,我们可以为多个线程间建立不同的Condition。
如果采用Object类中的wait(), notify(), notifyAll()实现的话,当写入数据之后需要唤醒读线程时,不可能通过notify()或notifyAll()明确的指定唤醒读线程,而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。所以,通过Condition能够更加精细的控制多线程的休眠与唤醒。
以上就是Java Lock锁Condition接口和newCondition()方法的详解。