用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。从而实现了两类线程的交替唤醒。