读写锁一般用于抢占资源的读写问题,使用它避免并发造成的数据错误。ReentrantReadWriteLock锁使得写操作具有原子性,使得读操作具有并发性,而且使得写操作的时候,不会有读操作,读操作的时候不会有写。
看下面的例子:
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by lizhiqiang on 2016/12/22. */ public class testReentrantReadWriteLock { public static void main(String[] args){ CountDownLatch countdownlatch = new CountDownLatch(5); ReadWriteLock lock = new ReentrantReadWriteLock(); ExecutorService service = Executors.newFixedThreadPool(10); MyCount myCount = new MyCount("9129129",10000); User u1 = new User("张三", myCount, -4000, lock, false); User u2 = new User("张三他爹", myCount, 6000, lock, false); User u3 = new User("张三他弟", myCount, -8000, lock, false); User u4 = new User("张三", myCount, 800, lock, false); User u5 = new User("张三他爹", myCount, 0, lock, true); service.execute(u1); service.execute(u2); service.execute(u3); service.execute(u4); service.execute(u5); service.shutdown(); } private static class User implements Runnable{ private String name; private MyCount myCount; private int money; private ReadWriteLock lock; private boolean isCheck; public User(String name, MyCount myCount, int money, ReadWriteLock lock, boolean isCheck) { this.name = name; this.myCount = myCount; this.money = money; this.lock = lock; this.isCheck = isCheck; } @Override public void run() { if(isCheck){ lock.readLock().lock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("读:" + name + "正在查询" + myCount + ",当前金额为" + myCount.getMoney()); lock.readLock().unlock(); }else{ lock.writeLock().lock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } int yue = this.myCount.getMoney(); System.out.println("写:"+name+"正在写"+myCount+"账户,金额为"+this.money+",当前余额为"+yue); int afteryue = yue+this.money; this.myCount.setMoney(afteryue); System.out.println("写:"+name+"正在写"+myCount+"账户成功,金额为"+this.money+",当前余额为"+afteryue); lock.writeLock().unlock(); } } } private static class MyCount { private String id ; private int money; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } public String getId() { return id; } public void setId(String id) { this.id = id; } public MyCount(String id, int money) { this.id = id; this.money = money; } public String toString(){ return "MyCount{oid="+this.id+",cash="+this.money+"}"; } } }输出结果是
写:张三正在写MyCount{oid=9129129,cash=10000}账户,金额为-4000,当前余额为10000
写:张三正在写MyCount{oid=9129129,cash=6000}账户成功,金额为-4000,当前余额为6000
写:张三他爹正在写MyCount{oid=9129129,cash=6000}账户,金额为6000,当前余额为6000
写:张三他爹正在写MyCount{oid=9129129,cash=12000}账户成功,金额为6000,当前余额为12000
写:张三他弟正在写MyCount{oid=9129129,cash=12000}账户,金额为-8000,当前余额为12000
写:张三他弟正在写MyCount{oid=9129129,cash=4000}账户成功,金额为-8000,当前余额为4000
写:张三正在写MyCount{oid=9129129,cash=4000}账户,金额为800,当前余额为4000
写:张三正在写MyCount{oid=9129129,cash=4800}账户成功,金额为800,当前余额为4800
读:张三他爹正在查询MyCount{oid=9129129,cash=4800},当前金额为4800
结果的顺序会改变,但是它们最终的结果是不变的。
有人说,这里如果把锁都去掉,然后将每一步操作,都换成对象的get方法,一样不会出错,而且速度更快。
:
其实未必不会出错,只是因为从变量里面get数据比较快,所以出错的几率不大而已。比如
this.myCount.setMoney(this.myCount.getMoney()+);this.money
这里运行速度很快,所以执行多次没有发现错误。
但是如果在getMoney方法里面加上等待时间,就会出现错误了。具体web开发中,一般是从数据库中获取以及插入操作的,速度相对比较慢,因此不加锁就会出错。
此外,有人问为什么读的时候不能写?
这个需要考虑数据读的内容会不会部分被写修改,那样读出来的就是脏数据了。