很多时候有这样一个场景: 一个共享资源,比如说一个文件或者一段文字,读线程可以并发执行,并发得查询读取,但是写线程写入的话,只能一个线程操作,并且互斥读线程。java的ReadWriteLock读写锁就是为这种场景准备的。
直接上读写锁的代码
package codeTest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @ClassName: ThreadTest3 * @Description: TODO 生产者 消费者线程测试 * @author xuejupo [email protected] * @date 2015-10-30 下午4:37:33 * */ public class ThreadTestLock { /** * 读写锁 */ static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false); static Lock r = rwl.readLock(); static Lock w = rwl.writeLock(); // 计数器,用于生产线程退出 private static int pCount = 0; // 计数器,用于消费退出 private static int cCount = 0; // 线程锁 // private static Object key = new Object(); // 资源库最大值 private static final int MAX_SIZE = 100; // 生产者,消费者共同拥有的资源 private static List<Object> list = new ArrayList<Object>(MAX_SIZE); public static void main(String[] a) { long start = System.currentTimeMillis(); producer p = new ThreadTestLock().new producer(1); producer p2 = new ThreadTestLock().new producer(2); producer p3 = new ThreadTestLock().new producer(3); consumer c1 = new ThreadTestLock().new consumer(1); consumer c2 = new ThreadTestLock().new consumer(2); consumer c3 = new ThreadTestLock().new consumer(3); c1.start(); c2.start(); c3.start(); p.start(); p2.start(); p3.start(); try { //等待线程全部完成再开始主线程 c1.join(); c2.join(); c3.join(); p.join(); p2.join(); p3.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("程序一共执行了:"+(end - start)+"毫秒"); } /** * 生产者线程 * * @ClassName: producer * @Description: TODO(这里用一句话描述这个类的作用) * @author xuejupo [email protected] * @date 2015-10-31 上午12:04:48 * */ class producer extends Thread { private int i; public producer(int i) { this.i = i; } public void run() { while (pCount <= 30) { // synchronized (key) { try { rwl.writeLock().lock(); pCount++; if (list.size() == MAX_SIZE) { System.out.println("生产者" + i + "获取权限,但是资源库中资源已满,等待消费者消费;"); Thread.sleep(1000); // key.wait(); } else { list.add(new Object()); System.out .println("生产者" + i + "获取权限,向资源库放资源;资源库中现有资源个数为:" + list.size()); Thread.sleep(1000); // key.notify(); } } catch (Exception e) { System.out.println("error---------------------"); }finally{ rwl.writeLock().unlock(); } } // } } } /** * 消费者线程 * * @ClassName: consumer * @Description: TODO(这里用一句话描述这个类的作用) * @author xuejupo [email protected] * @date 2015-10-31 上午12:05:03 * */ class consumer extends Thread { private int i; public consumer(int i) { this.i = i; } public void run() { while (cCount <= 30) { rwl.readLock().lock(); // synchronized (key) { cCount++; System.out.println(cCount); if (list.size() == 0) { System.out.println("消费者" + i + "获取权限,但是资源库中没有资源,等待生产;"); try { Thread.sleep(1000); // key.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("error---------------------"); }finally{ System.out.println("finally"); rwl.readLock().unlock(); } } else { System.out.println("消费者" + i + "获取权限,现在资源库有资源;" + list.size() + "个,消费1个资源"); list.remove(list.size() - 1); try { Thread.sleep(1000); // key.notify(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("error---------------------"); }finally{ rwl.readLock().unlock(); } } } // } } } }
结果:
1 消费者1获取权限,但是资源库中没有资源,等待生产; 2 消费者2获取权限,但是资源库中没有资源,等待生产; 3 消费者3获取权限,但是资源库中没有资源,等待生产; finally finally finally 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:1 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:2 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:3 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:4 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:5 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:6 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:7 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:8 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:9 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:10 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:11 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:12 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:13 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:14 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:15 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:16 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:17 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:18 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:19 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:20 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:21 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:22 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:23 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:24 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:25 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:26 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:27 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:28 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:29 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:30 生产者1获取权限,向资源库放资源;资源库中现有资源个数为:31 生产者2获取权限,向资源库放资源;资源库中现有资源个数为:32 4 5 消费者1获取权限,现在资源库有资源;32个,消费1个资源 6 消费者3获取权限,现在资源库有资源;31个,消费1个资源 消费者2获取权限,现在资源库有资源;32个,消费1个资源 生产者3获取权限,向资源库放资源;资源库中现有资源个数为:30 7 消费者3获取权限,现在资源库有资源;30个,消费1个资源 9 消费者1获取权限,现在资源库有资源;29个,消费1个资源 8 消费者2获取权限,现在资源库有资源;28个,消费1个资源 10 消费者3获取权限,现在资源库有资源;27个,消费1个资源 11 消费者2获取权限,现在资源库有资源;26个,消费1个资源 10 消费者1获取权限,现在资源库有资源;25个,消费1个资源 12 13 消费者3获取权限,现在资源库有资源;24个,消费1个资源 消费者1获取权限,现在资源库有资源;24个,消费1个资源 14 消费者2获取权限,现在资源库有资源;22个,消费1个资源 15 17 16 消费者1获取权限,现在资源库有资源;21个,消费1个资源 消费者2获取权限,现在资源库有资源;21个,消费1个资源 消费者3获取权限,现在资源库有资源;21个,消费1个资源 19 20 消费者3获取权限,现在资源库有资源;18个,消费1个资源 18 消费者1获取权限,现在资源库有资源;18个,消费1个资源 消费者2获取权限,现在资源库有资源;17个,消费1个资源 21 消费者3获取权限,现在资源库有资源;15个,消费1个资源 23 消费者1获取权限,现在资源库有资源;14个,消费1个资源 22 消费者2获取权限,现在资源库有资源;13个,消费1个资源 24 26 25 消费者2获取权限,现在资源库有资源;12个,消费1个资源 消费者1获取权限,现在资源库有资源;12个,消费1个资源 消费者3获取权限,现在资源库有资源;12个,消费1个资源 27 消费者2获取权限,现在资源库有资源;9个,消费1个资源 28 消费者3获取权限,现在资源库有资源;8个,消费1个资源 29 消费者1获取权限,现在资源库有资源;7个,消费1个资源 30 消费者2获取权限,现在资源库有资源;6个,消费1个资源 31 32 消费者3获取权限,现在资源库有资源;5个,消费1个资源 消费者1获取权限,现在资源库有资源;5个,消费1个资源 程序一共执行了:44009毫秒
结果分析:
1. 首先,代码里面的读线程,其实是应该互斥的。所以这种读写锁的形式并不适用于所有场景。他最适用的场景是读操作不互斥,比如单纯得读取一段文字信息的操作,而且更适用于写操作较少的操作。在写操作足够少的并行运算里,他可以最大限度得提高性能。如果写操作过多,获取写锁过多,和普通的并行操作就区别不大了。
2.在本代码里面,3个生产者和3个消费者分别运行,生产者获取写锁,消费者获取读锁,和我以前的博客http://709002341.iteye.com/admin/blogs/2253371对比一下。上一篇博客萝莉普通的生产者和消费者共执行了66604毫秒(博客里没有,我自己在本地加语句执行的),虽然写锁和读锁一样多,读写锁也占据明显优势。当写锁越少的情况下,这种优势越明显