ReentrantLocak
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
ReentrantLock锁在同一个时间点只能被一个线程持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。他们最大的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其他线程就必须等待);ReentrantLock是通过一个FIFO的等待队列来管理获取该锁的所有线程的。在“公平锁”的机制下,线程一次排队获取;而“非公平锁”在锁是可获取状态的时候,不管自己是不是在队列开头都会获取锁。
ReentrantLock的函数列表
/** * 创建一个ReentrantLock,默认是“非公平锁” */ public ReentrantLock() { sync = new NonfairSync(); } /** * 创建策略是faire的ReentrantLock。fair为true表示是公平锁,为false表示为非公平锁 */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } /** * 获取锁 */ public void lock() { sync.lock(); } /** * 如果当前线程未被中断则获取锁 */ public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } /** * 仅在调用时,锁未被另一个线程保持的状况下才获取锁 */ public boolean tryLock() { return sync.nonfairTryAcquire(1); } /** * 如果锁在给定的时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁 */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } /** * 视图释放此锁 */ public void unlock() { sync.release(1); } /** * 返回与此Lock实例一起使用的Condition实例 */ public Condition newCondition() { return sync.newCondition(); } /** * 查询当前线程保持此锁的次数 */ public int getHoldCount() { return sync.getHoldCount(); } /** * 该锁是否被当前线程保持 */ public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } /** * 查询此锁是否由任意线程保持 */ public boolean isLocked() { return sync.isLocked(); } /** * 判断是否是公平锁 */ public final boolean isFair() { return sync instanceof FairSync; } /** * 返回目前拥有此锁的线程 */ protected Thread getOwner() { return sync.getOwner(); } /** * 查询是否有些线程正在等待获取此锁 */ public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** * 查询给定线程是否正在等待获取此锁 */ public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** * 返回正在等待获取此锁的线程估计数 */ public final int getQueueLength() { return sync.getQueueLength(); } /** * 返回包含可能正在等待的线程的集合 * * @return the collection of threads */ protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); } /** * 查询是否有线程正在等待 */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * 返回等待与此锁相关的给定条件的线程估计数 */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); }
实例:认识lock和unlock
第一版:
class Depot{ private Integer size;//仓库的实际数量 private Lock lock;//独占锁 public Depot() { this.size = 0; this.lock = new ReentrantLock(); } public void produce(int val){ lock.lock();//加锁 try { size += val; System.out.println(Thread.currentThread().getName()+","+val +","+size); } finally { lock.unlock();//释放锁 } } public void consume(int val){ lock.lock(); try { size -= val; System.out.println(Thread.currentThread().getName()+","+val +","+size); } finally { lock.unlock(); } } } class producer{ private Depot depot; public producer(Depot depot) { this.depot = depot; } //新建一个线程向仓库中生产产品 public void produce(int val){ new Thread(){ @Override public void run(){ depot.produce(val); } }.start(); } } class cunsumer{ private Depot depot; public cunsumer(Depot depot) { this.depot = depot; } //新建一个线程向仓库中生产产品 public void consume(final int val){ new Thread(){ @Override public void run(){ depot.consume(val); } }.start(); } } public class TestLock { public static void main(String[] args) { Depot depot = new Depot(); producer p = new producer(depot); cunsumer c = new cunsumer(depot); p.produce(60); p.produce(120); c.consume(90); c.consume(150); p.produce(110); } }
1)Depot仓库通过produce向其中生产货物,通过consume消耗仓库中的货物。通过独占锁lock实现仓库的互斥访问:在操作仓库中的货物前会通过lock锁住仓库,操作完成之后通过unlock解锁。
2)producer通过调用produce方法新建一个线程向仓库中生产货物
3)consumer通过consume方法通过新建一个线程消耗仓库中的货物
4)主线程main中建一个消费者和生产者,分别用于生产和消耗。最终剩余50.
该模型中会有仓库为负和容量无限制的问题
第二版:解决上面两个问题,通过Condition来解决,通过Condition的await( )方法,让线程阻塞(类似于wait());通过signal( )方法让线程唤醒(类似于notify())。
class Depot{ private Integer capacity;//仓库的容量 private Integer size;//仓库的实际数量 private Lock lock;//独占锁 private Condition fullCondition;//生产条件 private Condition emptyCondition;//消耗的条件 public Depot(Integer capacity) { this.capacity = capacity; this.size = 0; this.lock = new ReentrantLock(); this.fullCondition = lock.newCondition(); this.emptyCondition = lock.newCondition(); } public void produce(int val){ lock.lock();//加锁 try { //wantProduceVal表示想要生产的数量(可能量太多需要多次生产) int wantProduceVal=val; while (wantProduceVal>0){ //实际数量大于等于容量,库满,等待 while (size>=capacity){ fullCondition.await(); } //获取实际生产的量,(即库存中新增的量) //如果 库存+想要生产的量>库容量,则 实际的增量=库容量-库存, //否则,实际增量=想要生产的量 int inc = (size+wantProduceVal)>capacity?(capacity-size):wantProduceVal; size+=inc; wantProduceVal-=inc; //size += val; System.out.println(Thread.currentThread().getName()+","+val +","+size); //通知消费者消费 emptyCondition.signal(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//释放锁 } } public void consume(int val){ lock.lock(); try { //表示想要消耗的量(可能量太大) int wantConsumeVal = val; //没有库存则等待生产 while (size<0) { emptyCondition.await(); } //获取实际消耗的量(即库存中实际减少的量) //如果 存库<要消耗的量,那么消耗量等于库存 //否则,减少的量就是要消耗的量 int dec = (wantConsumeVal>size)?size:wantConsumeVal; size-=dec; wantConsumeVal-=dec; //size -= val; System.out.println(Thread.currentThread().getName()+","+val +","+size); //通知生产者生产 fullCondition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } class producer{ private Depot depot; public producer(Depot depot) { this.depot = depot; } //新建一个线程向仓库中生产产品 public void produce(int val){ new Thread(){ @Override public void run(){ depot.produce(val); } }.start(); } } class cunsumer{ private Depot depot; public cunsumer(Depot depot) { this.depot = depot; } //新建一个线程向仓库中生产产品 public void consume(final int val){ new Thread(){ @Override public void run(){ depot.consume(val); } }.start(); } } public class TestLock { public static void main(String[] args) { Depot depot = new Depot(100); producer p = new producer(depot); cunsumer c = new cunsumer(depot); p.produce(60); p.produce(120); c.consume(90); c.consume(150); p.produce(110); } }