2.5. ReentrantLock
2.5.1. ReentrantLock介绍
1)基本介绍
在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,但是在JDK1.5中增加ReentrantLock类也可以达到同样的效果,并且在扩展功能上更加强大,比如有嗅探锁定、多路分支通知等,而且使用上比synchronized更加灵活。
2)样例程序
public class ReentrantBaseUsing {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
ReentrantBaseThread threadA = new ReentrantBaseThread(lock);
threadA.setName("A");
threadA.start();
ReentrantBaseThread threadB = new ReentrantBaseThread(lock);
threadB.setName("B");
threadB.start();
}
}
class ReentrantBaseThread extends Thread{
private Lock lock ;
public ReentrantBaseThread(Lock lock){
this.lock = lock;
}
@Override
public void run(){
lock.lock();
for (int i = 0; i < 2; i++) {
try {
System.out.println("线程"+Thread.currentThread().getName()+"打印"+i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
2.5.2. ReentrantLock实现wait和notify
1)基本介绍
关键字synchronized与wait/notify方法相结合可以实现等待/通知模式,类ReentrantLock也可以利用Condition对象实现。Condition类可以实现多路通知功能,也就是在一个Lock对象中创建多个Condition实例,线程对象可以注册在指定的Condition中,从而有选择性的进行线程通知,在调试线程上更加灵活。
2)与synchronized的对比
Synchronized相当于整个Lock对象中只有一个单一Condition对象,所有的线程都是注册在它一个对象的身上,线程开始notifyAll时就需要通知所有正在等待的线程,被通知的线程是由JVM随机选择的,而且没有选择权,因此会出现效率低问题。但使用ReentrantLock结合Condition类可以实现有选择性的通知,是在Condition中默认提供的。
3)样例程序
public class WaitAndNotifyInReentrant {
public static void main(String[] args) throws InterruptedException {
WAndNInReentrantService service = new WAndNInReentrantService();
WInReentrantThread waitThread = new WInReentrantThread(service);
waitThread.start();
Thread.sleep(1000);
NInReentrantThread notifyThread = new NInReentrantThread(service);
notifyThread.start();
}
}
class WAndNInReentrantService{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try{
lock.lock();
System.out.println("await方法开始....");
condition.await();
System.out.println("await方法结束");
lock.unlock();
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void signal(){
lock.lock();
condition.signal();
System.out.println("signal方法唤醒");
lock.unlock();
}
}
class WInReentrantThread extends Thread{
private WAndNInReentrantService service;
public WInReentrantThread(WAndNInReentrantService service){
this.service = service;
}
@Override
public void run(){
this.service.await();
}
}
class NInReentrantThread extends Thread{
private WAndNInReentrantService service;
public NInReentrantThread(WAndNInReentrantService service){
this.service = service;
}
@Override
public void run(){
this.service.signal();
}
}
4)注意事项
a.ReentrantLock类中使用await和signal方法需要在同步锁机制中才能使用。
2.5.3. 休眠线程分别唤醒
1)实现原理
通过多个condition对象实现不同线程的分别唤醒,也就是Condition对象可以唤醒部分指定的线程,这有助于提高程序的运行效率。
2)样例程序
public class MultiThreadNotify {
public static void main(String[] args) throws InterruptedException {
MultiThreadNotifyService service = new MultiThreadNotifyService();
MultiAConditionIsNotifyThread ThreadA = new MultiAConditionIsNotifyThread(service);
ThreadA.setName("A");
ThreadA.start();
MultiBConditionIsNotifyThread ThreadB = new MultiBConditionIsNotifyThread(service);
ThreadB.setName("B");
ThreadB.start();
Thread.sleep(1000);
service.signalAllA();
service.signalAllB();
}
}
class MultiThreadNotifyService{
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的await方法开始....");
conditionA.await();
System.out.println("线程"+Thread.currentThread().getName()+"的await方法结束");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的await方法开始....");
conditionB.await();
System.out.println("线程"+Thread.currentThread().getName()+"的await方法结束");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAllA(){
lock.lock();
conditionA.signal();
System.out.println("所有关于ConditionA的线程全被signal方法唤醒");
lock.unlock();
}
public void signalAllB(){
lock.lock();
conditionB.signal();
System.out.println("所有关于ConditionB的线程全被signal方法唤醒");
lock.unlock();
}
}
class MultiAConditionIsNotifyThread extends Thread{
private MultiThreadNotifyService service;
public MultiAConditionIsNotifyThread(MultiThreadNotifyService service){
this.service = service;
}
@Override
public void run(){
this.service.awaitA();
}
}
class MultiBConditionIsNotifyThread extends Thread{
private MultiThreadNotifyService service;
public MultiBConditionIsNotifyThread(MultiThreadNotifyService service){
this.service = service;
}
@Override
public void run(){
this.service.awaitB();
}
}
2.5.4. Lock实现多消费者多生产者
1) 实现原理
基于ReentrantLock和Lock实现多生产者和多消费者。
2)样例程序
public class MultiPAndCInLock {
public static void main(String[] args) {
MultiPAndCInLockService service = new MultiPAndCInLockService();
int size = 5;
MultiPInLockThread[] multiPThread = new MultiPInLockThread[size];
MultiCInLockThread[] multiCThread = new MultiCInLockThread[size];
for (int i = 0; i < size; i++) {
multiPThread[i] = new MultiPInLockThread(service);
multiPThread[i].setName("A"+i);
multiPThread[i].start();
multiCThread[i] = new MultiCInLockThread(service);
multiCThread[i].setName("B"+i);
multiCThread[i].start();
}
}
}
class MultiPAndCInLockService{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private String data = "";
public void productor(){
try{
lock.lock();
while(!"".equals(data)){
System.out.println("生产者"+Thread.currentThread().getName()+"中还有剩余数据未被消费");
condition.await();
}
System.out.println("生产者"+Thread.currentThread().getName()+"开始生产数据");
data = "data";
condition.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void consumer(){
try{
lock.lock();
while("".equals(data)){
System.out.println("消费者"+Thread.currentThread().getName()+"等待生产者制造数据");
condition.await();
}
System.out.println("消费者"+Thread.currentThread().getName()+"开始消费数据");
data = "";
condition.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
class MultiPInLockThread extends Thread{
private MultiPAndCInLockService service;
public MultiPInLockThread(MultiPAndCInLockService service){
this.service = service;
}
@Override
public void run(){
this.service.productor();
}
}
class MultiCInLockThread extends Thread{
private MultiPAndCInLockService service;
public MultiCInLockThread(MultiPAndCInLockService service){
this.service = service;
}
@Override
public void run(){
this.service.consumer();
}
}
2.5.5. 公平锁与非公平锁
1)基本介绍
Lock分为公平锁和非公平锁,公平锁:线程获取锁的顺序是按照加锁的顺序来分配的,先到先得;非公平锁:一种获取锁的抢占机制,是随机获取锁,可能造成有的线程一直得不到锁。
2)样例程序
public class FairLock {
public static void main(String[] args) {
FairLockService service = new FairLockService(true);
int size = 5;
FairThread[] threads = new FairThread[size];
for (int i = 0; i < size; i++) {
threads[i] = new FairThread(service);
}
for (int i = 0; i < size; i++) {
threads[i].start();
}
}
}
class FairLockService{
private Lock lock;
public FairLockService(boolean IsFairLock){
lock = new ReentrantLock(IsFairLock);
}
public void foo(){
lock.lock();
System.out.println("创建线程" + Thread.currentThread().getName());
lock.unlock();
}
}
class FairThread extends Thread{
private FairLockService service;
public FairThread(FairLockService service){
this.service = service;
}
@Override
public void run(){
this.service.foo();
}
}
2.6. ReentrantReadWriteLock
1)使用场景
ReentrantLock类具有完全互斥排他锁,也就是说同一时间内只有一个线程可以在运行ReentrantLock.lock方法后面的任务,这样虽然保证实例变量的线程安全性,但是效率十分低下。所以引入一种读写锁ReentrantReadWriteLock类,可以加快运行的效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁来提升方法代码的运行效率。
2)使用介绍
读写锁具有两类锁,一是读操作(也称共享锁),二是写操作(也称排他锁)。换言之,多个读操锁之间不互斥,但是写锁与写锁之间、读锁与写锁之间会发生互斥。
3)样例程序
public class ReentrantReadAndWriteLock {
public static void main(String[] args) throws InterruptedException {
ReadWriteLockService service = new ReadWriteLockService();
int size = 2;
readLockThread[] readThread = new readLockThread[size];
for (int i = 0; i < size; i++) {
readThread[i] = new readLockThread(service);
readThread[i].setName("读线程"+i);
readThread[i].start();
}
Thread.sleep(3000);
System.out.println("=========================================");
WriteLockThread[] writeThread = new WriteLockThread[size];
for (int i = 0; i < size; i++) {
writeThread[i] = new WriteLockThread(service);
writeThread[i].setName("写线程"+i);
writeThread[i].start();
}
}
}
class ReadWriteLockService{
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read(){
try{
lock.readLock().lock();
System.out.println(Thread.currentThread().getName()+"开启读锁");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"退出读锁");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
public void write(){
try{
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"开启写锁");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"退出写锁");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
}
class readLockThread extends Thread{
private ReadWriteLockService service;
public readLockThread(ReadWriteLockService service){
this.service = service;
}
@Override
public void run(){
this.service.read();
}
}
class WriteLockThread extends Thread{
private ReadWriteLockService service;
public WriteLockThread(ReadWriteLockService service){
this.service = service;
}
@Override
public void run(){
this.service.write();
}
}