转载自 https://www.cnblogs.com/skywang12345/p/3496101.html
ReentrantLock介绍
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
ReentrantLock函数列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| ReentrantLock()
ReentrantLock(boolean fair)
int getHoldCount()
protected Thread getOwner()
protected Collection<Thread> getQueuedThreads()
int getQueueLength()
protected Collection<Thread> getWaitingThreads(Condition condition)
int getWaitQueueLength(Condition condition)
boolean hasQueuedThread(Thread thread)
boolean hasQueuedThreads()
boolean hasWaiters(Condition condition)
boolean isFair()
boolean isHeldByCurrentThread()
boolean isLocked()
void lock()
void lockInterruptibly()
Condition newCondition()
boolean tryLock()
boolean tryLock(long timeout, TimeUnit unit)
void unlock()
|
ReentrantLock示例
通过对比“示例1”和“示例2”,我们能够清晰的认识lock和unlock的作用
示例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
class Depot { private int 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.printf("%s produce(%d) --> size=%d\n", Thread.currentThread().getName(), val, size); } finally { lock.unlock(); } }
public void consume(int val) { lock.lock(); try { size -= val; System.out.printf("%s consume(%d) <-- size=%d\n", Thread.currentThread().getName(), val, size); } finally { lock.unlock(); } } };
class Producer { private Depot depot;
public Producer(Depot depot) { this.depot = depot; }
public void produce(final int val) { new Thread() { public void run() { depot.produce(val); } }.start(); } }
class Customer { private Depot depot;
public Customer(Depot depot) { this.depot = depot; }
public void consume(final int val) { new Thread() { public void run() { depot.consume(val); } }.start(); } }
public class LockTest1 { public static void main(String[] args) { Depot mDepot = new Depot(); Producer mPro = new Producer(mDepot); Customer mCus = new Customer(mDepot);
mPro.produce(60); mPro.produce(120); mCus.consume(90); mCus.consume(150); mPro.produce(110); } }
|
运行结果:
1 2 3 4 5
| Thread-0 produce(60) --> size=60 Thread-1 produce(120) --> size=180 Thread-3 consume(150) <-- size=30 Thread-2 consume(90) <-- size=-60 Thread-4 produce(110) --> size=50
|
结果分析:
(01) Depot 是个仓库。通过produce()能往仓库中生产货物,通过consume()能消费仓库中的货物。通过独占锁lock实现对仓库的互斥访问:在操作(生产/消费)仓库中货品前,会先通过lock()锁住仓库,操作完之后再通过unlock()解锁。
(02) Producer是生产者类。调用Producer中的produce()函数可以新建一个线程往仓库中生产产品。
(03) Customer是消费者类。调用Customer中的consume()函数可以新建一个线程消费仓库中的产品。
(04) 在主线程main中,我们会新建1个生产者mPro,同时新建1个消费者mCus。它们分别向仓库中生产/消费产品。
根据main中的生产/消费数量,仓库最终剩余的产品应该是50。运行结果是符合我们预期的!
这个模型存在两个问题:
(01) 现实中,仓库的容量不可能为负数。但是,此模型中的仓库容量可以为负数,这与现实相矛盾!
(02) 现实中,仓库的容量是有限制的。但是,此模型中的容量确实没有限制的!
这两个问题,我们稍微会讲到如何解决。现在,先看个简单的示例2;通过对比“示例1”和“示例2”,我们能更清晰的认识lock(),unlock()的用途。
示例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
class Depot { private int size; private Lock lock;
public Depot() { this.size = 0; this.lock = new ReentrantLock(); }
public void produce(int val) {
size += val; System.out.printf("%s produce(%d) --> size=%d\n", Thread.currentThread().getName(), val, size);
}
public void consume(int val) {
size -= val; System.out.printf("%s consume(%d) <-- size=%d\n", Thread.currentThread().getName(), val, size);
} };
class Producer { private Depot depot;
public Producer(Depot depot) { this.depot = depot; }
public void produce(final int val) { new Thread() { public void run() { depot.produce(val); } }.start(); } }
class Customer { private Depot depot;
public Customer(Depot depot) { this.depot = depot; }
public void consume(final int val) { new Thread() { public void run() { depot.consume(val); } }.start(); } }
public class LockTest2 { public static void main(String[] args) { Depot mDepot = new Depot(); Producer mPro = new Producer(mDepot); Customer mCus = new Customer(mDepot);
mPro.produce(60); mPro.produce(120); mCus.consume(90); mCus.consume(150); mPro.produce(110); } }
|
(某一次)运行结果:
1 2 3 4 5
| Thread-0 produce(60) --> size=-60 Thread-4 produce(110) --> size=50 Thread-2 consume(90) <-- size=-60 Thread-1 produce(120) --> size=-60 Thread-3 consume(150) <-- size=-60
|
结果说明:
“示例2”在“示例1”的基础上去掉了lock锁。在“示例2”中,仓库中最终剩余的产品是-60,而不是我们期望的50。原因是我们没有实现对仓库的互斥访问。
示例3
在“示例3”中,我们通过Condition去解决“示例1”中的两个问题:“仓库的容量不可能为负数”以及“仓库的容量是有限制的”。
解决该问题是通过Condition。Condition是需要和Lock联合使用的:通过Condition中的await()方法,能让线程阻塞[类似于wait()];通过Condition的signal()方法,能让唤醒线程[类似于notify()]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition;
class Depot { private int capacity; private int size; private Lock lock; private Condition fullCondtion; private Condition emptyCondtion;
public Depot(int capacity) { this.capacity = capacity; this.size = 0; this.lock = new ReentrantLock(); this.fullCondtion = lock.newCondition(); this.emptyCondtion = lock.newCondition(); }
public void produce(int val) { lock.lock(); try { int left = val; while (left > 0) { while (size >= capacity) fullCondtion.await(); int inc = (size+left)>capacity ? (capacity-size) : left; size += inc; left -= inc; System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, inc, size); emptyCondtion.signal(); } } catch (InterruptedException e) { } finally { lock.unlock(); } }
public void consume(int val) { lock.lock(); try { int left = val; while (left > 0) { while (size <= 0) emptyCondtion.await(); int dec = (size<left) ? size : left; size -= dec; left -= dec; System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, dec, size); fullCondtion.signal(); } } catch (InterruptedException e) { } finally { lock.unlock(); } }
public String toString() { return "capacity:"+capacity+", actual size:"+size; } };
class Producer { private Depot depot;
public Producer(Depot depot) { this.depot = depot; }
public void produce(final int val) { new Thread() { public void run() { depot.produce(val); } }.start(); } }
class Customer { private Depot depot;
public Customer(Depot depot) { this.depot = depot; }
public void consume(final int val) { new Thread() { public void run() { depot.consume(val); } }.start(); } }
public class LockTest3 { public static void main(String[] args) { Depot mDepot = new Depot(100); Producer mPro = new Producer(mDepot); Customer mCus = new Customer(mDepot);
mPro.produce(60); mPro.produce(120); mCus.consume(90); mCus.consume(150); mPro.produce(110); } }
|
(某一次)运行结果:
1 2 3 4 5 6 7 8 9 10
| Thread-0 produce( 60) --> left= 0, inc= 60, size= 60 Thread-1 produce(120) --> left= 80, inc= 40, size=100 Thread-2 consume( 90) <-- left= 0, dec= 90, size= 10 Thread-3 consume(150) <-- left=140, dec= 10, size= 0 Thread-4 produce(110) --> left= 10, inc=100, size=100 Thread-3 consume(150) <-- left= 40, dec=100, size= 0 Thread-4 produce(110) --> left= 0, inc= 10, size= 10 Thread-3 consume(150) <-- left= 30, dec= 10, size= 0 Thread-1 produce(120) --> left= 0, inc= 80, size= 80 Thread-3 consume(150) <-- left= 0, dec= 30, size= 50
|
代码中的已经包含了很详细的注释,这里就不再说明了。