用ReentrantLock和Condition实现线程间通信

在Java多线程中,除了使用synchronize关键字来实现线程之间的同步互斥,还可以使用JDK1.5中新增的RetrantLock类来实现同样的效果。RetrantLock类的扩展功能也更加强大,比如具有嗅探锁定,多路分支通知等功能,在使用上也比synchronize更为灵活。

借助于Condition对象,RetrantLock可以实现类似于Object的wait和notify/notifyAll功能。使用它具有更好的灵活性,在一个Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition对象中,从而可以有选择性的进行线程通知,实现多路通知功能,在调度线程上更加灵活。

每一个 Lock 可以有任意数据的 Condition 对象, Condition 是与 Lock 绑定的,所以就有 Lock 的公平性特性:如果是公平锁,线程为按照FIFO的顺序从 Condition.await 中释放,如果是非公平锁,那么后续的锁竞争就不保证FIFO顺序了。

下面是一个生产者、消费者的示例:

/*
 * 买家线程,当书店中有书的时候买书
 */
public class Buyer extends Thread {
    private BookStore bookStore;

    public Buyer(BookStore bookStore) {
        this.bookStore = bookStore;
    }

    @Override
    public void run() {
        while (true) {
            bookStore.removeBook();
        }
    }
}
/*
 * 售货员线程,当书店中还有空位,买进书籍
 */
public class Seller extends Thread {
    private BookStore bookStore;
    public Seller(BookStore bookStore) {
        this.bookStore = bookStore;
    }

    @Override
    public void run() {
        while (true) {
            bookStore.addBook();
        }
    }
}
import java.util.ArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 书店类
 */
public class BookStore {
    private ArrayList books = new ArrayList();
    private ReentrantLock lock = new ReentrantLock(false);
    private Condition buyCondition = lock.newCondition();
    private Condition sellCondition = lock.newCondition();
    public void addBook() {
        lock.lock();
        while (books.size() >= 1) {
            try {
                System.out.println(Thread.currentThread().getName() + "等待图书售出");
                sellCondition.await();  //售货员等待书店出现空位
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        books.add(1);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "购入了一本书,剩余:" + books.size());
        buyCondition.signal();  //通知买家买书
        lock.unlock();
    }

    public void removeBook() {
        lock.lock();
        while (books.size() <= 0) {
            try {
                System.out.println(Thread.currentThread().getName() + "等待购入图书");
                buyCondition.await();  //买家等待书店进书
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        books.remove(0);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "买了一本书,剩余:" + books.size());
        sellCondition.signal();  //通知售货员进书
        lock.unlock();
    }
}
/*
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        BookStore bookStore = new BookStore();
        Buyer[]  buyers = new Buyer[5];
        Seller  sellers;
        //创建5个买家线程,负责买书
        for(int i = 0; i < 5; i++) {
            buyers[i] = new Buyer(bookStore);
        }
        //创建售货员线程,负责进书
        sellers = new Seller(bookStore);
        //启动线程
        sellers.start();
        for(int i = 0; i < 5; i++) {
            buyers[i].start();
        }
    }
}

运行结果:

从结果我们可以看到售货员线程和买家线程是交替运行的,这就是因为两类线程分别绑定了两个不同的Condition:buyCondition,sellCondition。从而实现了两类线程的交替唤醒。

原文地址:https://www.cnblogs.com/cisol/p/6673190.html