1.前言
java除了synchronized 锁外,还有Lock更加灵活的锁。
ReenTrantLock从名字上理解,是可再进入的锁。重入锁是一种递归无阻塞的同步机制,底层实现采用AQS原理实现,具体实现原理将在今后的文章中具体探讨。
ReentrantReadWriterLock:可重入读写锁。读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!ReentrantReadWriterLock的底层实现也是AQS内部状态state同步进行加锁。
2. ReenTrantLock 例子:
/** * lock * @author niyuelin * * */ public class ReentrantLockTest { public int inc = 0; Lock lock = new ReentrantLock(); public void increase() { lock.lock(); try { inc++; } finally { lock.unlock(); } } public static void main(String[] args) { final ReentrantLockTest test = new ReentrantLockTest(); for (int i = 0; i < 10; i++) { new Thread() { public void run() { for (int j = 0; j < 1000; j++) test.increase(); }; }.start(); } while (Thread.activeCount() > 2) // 保证前面的线程都执行完 Thread.yield(); System.out.println(test.inc); } }
输出:
10000
ReentrantLock必须在使用完后,在finally方法释放,否则别人无法使用该锁,或者重复调用导致死锁。
3.ReentrantReadWriteLock例子
/** * ReadWriteLock * @author niyuelin * */ public class ReentrantReadWriteLockTest { static class MyObject { private Object object; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void get() { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + "准备读数据!!"); try { Thread.sleep(new Random().nextInt(1000)); System.out.println(Thread.currentThread().getName() + "读数据为:" + this.object); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } public void put(Object object) { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + "准备写数据"); try { Thread.sleep(new Random().nextInt(1000)); this.object = object; System.out.println(Thread.currentThread().getName() + "写数据为" + this.object); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } } public static void main(String[] args) throws InterruptedException { final MyObject myObject = new MyObject(); ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 2; i++) { executor.execute(new Runnable() { @Override public void run() { for (int j = 0; j < 3; j++) myObject.put(new Random().nextInt(1000)); } }); } for (int i = 0; i < 2; i++) { executor.execute(new Runnable() { @Override public void run() { for (int j = 0; j < 3; j++) myObject.get(); } }); } executor.shutdown(); } }
输出:
pool-1-thread-2准备写数据
pool-1-thread-2写数据为759
pool-1-thread-2准备写数据
pool-1-thread-2写数据为209
pool-1-thread-1准备写数据
pool-1-thread-1写数据为602
pool-1-thread-1准备写数据
pool-1-thread-1写数据为431
pool-1-thread-4准备读数据!!
pool-1-thread-3准备读数据!!
pool-1-thread-3读数据为:431
pool-1-thread-4读数据为:431
pool-1-thread-2准备写数据
pool-1-thread-2写数据为430
pool-1-thread-1准备写数据
pool-1-thread-1写数据为757
pool-1-thread-3准备读数据!!
pool-1-thread-4准备读数据!!
pool-1-thread-4读数据为:757
pool-1-thread-4准备读数据!!
pool-1-thread-3读数据为:757
pool-1-thread-3准备读数据!!
pool-1-thread-3读数据为:757
pool-1-thread-4读数据为:757
写数据的时候只有一个线程能写,别人不能读。读数据的时候别人不能写,但是可以读
4.总结
ReentrantLock 作为重用锁,在实际开发当中是 synchronized锁的替代方案。
ReentrantReadWriteLock 适合用在对于高并发任务对内存的读写操作时使用。