锁
1、锁分为读锁和写锁
2、多个读锁不互斥
3、读锁和写锁互斥
4、写锁与写锁互斥
总之,这是由JVM自己控制的,如果你的代码只读取数据,可以多人同时读,但不能同时写,那就上读锁;若歌你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。
三个线程读数据,三个线程写数据示例:
功能:可以同时读,读的时候不能写,不能同时写,写的时候不能读,读的时候上读锁,读完解锁;写的时候上写锁,写完解锁。注意finally解锁
import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { /**读写所使用 * 三个线程读,三个线程写 */ public static void main(String[] args) { //共享对象 final Source source = new Source(); //创建线程 for (int i=0; i<3; i++){ //读 new Thread(new Runnable(){ public void run(){ while (true) source.get(); } }).start(); //写 new Thread(new Runnable(){ public void run(){ while (true) source.put(new Random().nextInt(999)); } }).start(); } } static class Source { //共享数据 private int data = 0; //要操作同一把锁上的读或写锁 ReadWriteLock rwl = new ReentrantReadWriteLock(); //读方法 public void get() { //上读锁 rwl.readLock().lock(); try { //获取数据并输出 System.out.println("读——"+Thread.currentThread().getName()+"正在获取数据。。。"); try { Thread.sleep(new Random().nextInt(6)*1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("读——"+Thread.currentThread().getName()+"获取到的数据:"+data); }finally { //解锁 rwl.readLock().unlock(); } } //写方法 public void put(int data) { //上写锁 rwl.writeLock().lock(); try { //提示信息 System.out.println("写——"+Thread.currentThread().getName()+"正在改写数据。。。"); try { Thread.sleep(new Random().nextInt(6)*1000); } catch (InterruptedException e) { e.printStackTrace(); } this.data = data; System.out.println("写——"+Thread.currentThread().getName()+"已将数据改写为:"+data); }finally { //解锁 rwl.writeLock().unlock(); } } } }
运行结果为:
读——Thread-0正在获取数据。。。
读——Thread-2正在获取数据。。。
读——Thread-4正在获取数据。。。
读——Thread-0获取到的数据:0
读——Thread-4获取到的数据:0
读——Thread-2获取到的数据:0
写——Thread-3正在改写数据。。。
写——Thread-3已将数据改写为:565
写——Thread-3正在改写数据。。。
ReentrantReadWriteLock
构造方法摘要 |
ReentrantReadWriteLock() 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。 |
ReentrantReadWriteLock(boolean fair) 使用给定的公平策略创建一个新的 ReentrantReadWriteLock。 |
方法摘要 | ||||
protected Thread | getOwner() 返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null。 | |||
protected Collection<Thread> | getQueuedReaderThreads() 返回一个 collection,它包含可能正在等待获取读取锁的线程。 | |||
protected Collection<Thread> | getQueuedThreads() 返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。 | |||
protected Collection<Thread> | getQueuedWriterThreads() 返回一个 collection,它包含可能正在等待获取写入锁的线程。 | |||
int | getQueueLength() 返回等待获取读取或写入锁的线程估计数目。 | |||
int | getReadHoldCount() 查询当前线程在此锁上保持的重入读取锁数量。 | |||
int | getReadLockCount() 查询为此锁保持的读取锁数量。 | |||
protected Collection<Thread> | getWaitingThreads(Condition condition) 返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。 | |||
int | getWaitQueueLength(Condition condition) 返回正等待与写入锁相关的给定条件的线程估计数目。 | |||
int | getWriteHoldCount() 查询当前线程在此锁上保持的重入写入锁数量。 | |||
boolean | hasQueuedThread(Thread thread) 查询是否给定线程正在等待获取读取或写入锁。 | |||
boolean | hasQueuedThreads() 查询是否所有的线程正在等待获取读取或写入锁。 | |||
boolean | hasWaiters(Condition condition) 查询是否有些线程正在等待与写入锁有关的给定条件。 | |||
boolean | isFair() 如果此锁将公平性设置为 ture,则返回 true。 | |||
boolean | isWriteLocked() 查询是否某个线程保持了写入锁。 | |||
boolean | isWriteLockedByCurrentThread() 查询当前线程是否保持了写入锁。 | |||
readLock() 返回用于读取操作的锁。 | ||||
toString() 返回标识此锁及其锁状态的字符串。 | ||||
writeLock() 返回用于写入操作的锁。 |
JDK帮助文档中的示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):
class CachedData { Object data; volatile boolean cacheValid; 数据有没有标记 ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() {处理数据 rwl.readLock().lock();先上读锁 if (!cacheValid) {如果数据不存在 // Must release read lock before acquiring write lock rwl.readLock().unlock();准备写数据,需先解除读锁 rwl.writeLock().lock();上写锁 // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) {再次检查数据是否存在,防止其他线程已经存入数据 data = ... cacheValid = true;写好数据,改变标记 } // Downgrade by acquiring read lock before releasing write lock 准备释放写锁,数据存在了,释放后就要使用数据,恢复产生数据前的读锁状态 rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data);存在直接使用数据 rwl.readLock().unlock();解除读锁 } }
面试题:设计一个缓存系统
缓存系统:你要取数据,需调用我的public Object getData(String key)方法,我要检查我内部有没有这个数据,如果有就直接返回,如果没有,就从数据库中查找这个数,查到后将这个数据存入我内部的存储器中,下次再有人来要这个数据,我就直接返回这个数不用再到数据库中找了。 你要取数据不要找数据库,来找我。
class CachedSystem { //缓存系统的存储器 private Map<String, Object> cache = new HashMap<String, Object>(); //取数据方法可能有多个线程来取数据,没有数据的话又会去数据库查询,需要互斥 public synchronized Object get(String key) { //先查询内部存储器中有没有要的值 Object value = cache.get(key); if (value==null)//如果没有,就去数据库中查询,并将查到的结果存入内部存储器中 { value = “aaaa”; //实际代码是查询后的结果 queryDB(key) cache.put(key, value); } return value; } //上面的代码每次只能有一个线程来查询,但只有写的时候才需要互斥,修改如下 //来一个读写锁 ReadWriteLock rwl = new ReentrantReadWriteLock(); public Object get(String key){ //上读锁 rwl.readLock().lock(); //先查询内部存储器中有没有要的值 Object value = cache.get(key); if (value==null)//如果没有,就去数据库中查询,并将查到的结果存入内部存储器中 { //释放读锁 上写锁 rwl.readLock().unlock(); rwl.writeLock().lock(); if (value==null)再次进行判断,防止多个写线程堵在这个地方重复写 { value = “aaaa”; cache.put(key, value); } //设置完成 释放写锁,恢复读写状态 rwl.readLock().lock(); rwl.writeLock().unlock(); } //释放读锁 rwl.readLock().unlock(); return value; //注意:try finally中unlock } }