点赞再看,养成好习惯!!!
Java5之前只能用synchronized和volatile,Java5后Doug Lea提供了ReentrantLock,并非为了代替内置锁,而是当内置锁的机制不实用时,作为一种可抉择的高级性能。
内置锁不实用的场景包含:
- 无奈中断一个正在期待获取锁的线程
- 有限的锁期待
- 内置锁必须放在代码块里(编程有些局限性)
所以提供了J.U.C的Lock接口及实现。
1. Lock和ReentrantLock
之所以叫ReentrantLock,可了解为两局部
- Re-entrant
可重入,lock多少次都没关系,只须要unlock即可,或者lock外面嵌套了别的lock都能够 - Lock
提供了和synchronized一样的互斥性和内存可见性,与synchronized的monitor内存语义一样
2 Synchronized(S) V.S Lock(L)
- L 是接口,S 是关键字
- S异样时,会主动开释线程占有的锁,不会产生死锁
L异样时,若没有被动通过 unlock()开释锁,则很有可能造成死锁。所以用 lock 时要在 finally 中开释锁.。 - L 能够当期待锁的线程响应中断
应用 S 时,期待的线程将会始终等上来,不能响应中断 - 通过 L 能够晓得是否胜利取得锁,S 不能够
- L 能够进步多个线程进行读写操作的效率
3 Lock的个性
- 可定时锁期待
- 可轮询锁期待
- 可中断锁期待
- 公平性
- 实现非块构造的加锁
- 绑定多个Condition。通过屡次newCondition能够取得多个Condition对象,能够简略的实现比较复杂的线程同步的性能.通过await(),signal();
3.1 轮询锁和定时锁
内置锁的死锁问题只能通过重启解决,可定时、可轮询锁提供了另一种抉择:
通过tryLock
解决
public class DeadlockAvoidance {
private static Random rnd = new Random();
public boolean transferMoney(Account fromAcct,
Account toAcct,
DollarAmount amount,
long timeout,
TimeUnit unit)
throws InsufficientFundsException, InterruptedException {
long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout); //定时,轮询
while (true) {
if (fromAcct.lock.tryLock()) {
try {
if (toAcct.lock.tryLock()) {
try {
if (fromAcct.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAcct.debit(amount);
toAcct.credit(amount);
return true;
}
} finally {
toAcct.lock.unlock();
}
}
} finally {
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime)
return false;
NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
}
}
private static final int DELAY_FIXED = 1;
private static final int DELAY_RANDOM = 2;
static long getFixedDelayComponentNanos(long timeout, TimeUnit unit) {
return DELAY_FIXED;
}
static long getRandomDelayModulusNanos(long timeout, TimeUnit unit) {
return DELAY_RANDOM;
}
static class DollarAmount implements Comparable<DollarAmount> {
public int compareTo(DollarAmount other) {
return 0;
}
DollarAmount(int dollars) {
}
}
class Account {
public Lock lock;
void debit(DollarAmount d) {
}
void credit(DollarAmount d) {
}
DollarAmount getBalance() {
return null;
}
}
class InsufficientFundsException extends Exception {
}
}
3.2 带有工夫限度的锁
3.3 可中断的锁
3.4对于Condition
最典型的就是阻塞的有界队列的实现。
public class BoundedBuffer {
private static final Logger logger = LoggerFactory.getLogger(BoundedBuffer.class);
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[2]; // 阻塞队列
int putptr, takeptr, count;
private void log(String info) {
logger.info(Thread.currentThread().getName() + " - " + info);
}
public void put(Object x) throws InterruptedException {
log(x + ",执行put");
lock.lock();
log(x + ",put lock.lock()");
try {
while (count == items.length) { // 如果队列满了,notFull就始终期待
log(x + ",put notFull.await() 队列满了");
notFull.await(); // 调用await的意思取反,及not notFull -> Full
}
items[putptr] = x; // 终于能够插入队列
if (++putptr == items.length) {
putptr = 0; // 如果下标达到数组边界,循环下标置为0
}
++count;
log(x + ",put胜利 notEmpty.signal() 周知队列不为空了");
notEmpty.signal(); // 唤醒notEmpty
} finally {
log(x + ",put lock.unlock()");
lock.unlock();
}
}
public Object take() throws InterruptedException {
log("执行take");
lock.lock();
Object x = null;
log("take lock.lock()");
try {
while (count == 0) {
log("take notEmpty.await() 队列为空等等");
notEmpty.await();
}
x = items[takeptr];
if (++takeptr == items.length) {
takeptr = 0;
}
--count;
log(x + ",take胜利 notFull.signal() 周知队列有残余空间了");
notFull.signal();
return x;
} finally {
lock.unlock();
log(x + ",take lock.unlock()");
}
}
public static void main(String[] args) throws InterruptedException {
final BoundedBuffer bb = new BoundedBuffer();
ExecutorService executor = Executors.newFixedThreadPool(10);
for (char i = 'A'; i < 'F'; i++) {
final char t = i;
executor.execute(() -> {
try {
bb.put(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
List<Character> res = new LinkedList<>();
for (char i = 'A'; i < 'F'; i++) {
executor.execute(() -> {
try {
char c = (char) bb.take();
res.add(c);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
try {
executor.awaitTermination(2, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
logger.info(res.toString());
executor.shutdownNow();
}
}
4 性能思考因素
Java5的时候J.U.C的ReentrantLock锁竞争性能十分好,到了Java6应用了改良后的算法来治理内置锁,所以当初差不太多了,只好一点点
竞争性能的影响可伸缩性的要害因素:如果有越多的资源被消耗在锁的治理和线程调度上,那么应用程序失去的资源就越少,锁的实现形式越好,将须要越少的零碎调用和上下文切换。
5 公平性
ReentrantLock默认创立非偏心的锁,非偏心指被阻塞挂起的线程(LockSupport.park)都在AQS的CLH队列中排队期待本人被唤醒。他们是依照收回的申请程序来排队的,但一旦有一个唤醒的就会和新来的线程竞争锁,新来的可能会“插队”。若新来的胜利获取锁,那么它将跳过所有期待线程而开始执行,这意味着本该被唤醒的线程失败了,对不起您回到队列的尾部持续等。
个别,非偏心锁的性能要好于偏心锁。因为一个线程被唤醒是须要工夫的,挂起线程和唤醒复原线程都存在开销,这个空隙如果有其余线程处于ready状态,无需上下文切换,那么间接运行就行。
A持有锁,B申请,但B在复原的过程中,C能够插队”非偏心”的获取锁,而后执行再开释,这时候B刚刚好做完上下文切换能够执行,这个对于B和C来说是一个“双赢”的场面,是进步吞吐量的起因。
JVM也没有在其内置锁上采纳公平性的机制。
6 选型
除非应用到3提到的高级个性,或者内置锁无奈满足需要时,否则还是诚实用内置锁,毕竟是JVM本身提供的,而不是靠类库,因而可能会执行一些优化。
另外内置锁在利用kill -3 dump thread的时候能够发现栈帧上的一些monitor lock的信息,辨认死锁,而J.U.C的锁这方面就不太行,当然JAVA6之后提供了治理和调试接口解决了。
7 读-写锁
ReentrantLock每次只有一个线程能持有锁,然而这种严格的互斥也会克制并发。会克制
- 写/写
- 写/读
- 读/读
抵触,然而很多状况下读操作是十分多的,如果放宽加锁的需要,容许多个读操作能够同时拜访数据,那么就能够晋升性能。然而要保障读取的数据是最新的,不会有其余线程批改数据。
应用ReadWriteLock的场景:
- 一个资源能够被多个读操作拜访
- 被一个写操作拜访
- 但二者不能同时进行
如果读线程正在持有锁,这时候另外一个写线程,那么会优先获取写锁:
public class ReadWriteMap<K, V> {
private final Map<K, V> map;
private final ReadWriteLock lock=new ReentrantReadWriteLock();
private final Lock r=lock.readLock();
private final Lock w=lock.writeLock();
public ReadWriteMap(Map<K, V> map) {
this.map=map;
}
public V put(K key, V value) {
w.lock();
try {
return map.put( key, value );
} finally {
w.unlock();
}
}
public V remove(Object key) {
w.lock();
try {
return map.remove( key );
} finally {
w.unlock();
}
}
public void putAll(Map<? extends K, ? extends V> m) {
w.lock();
try {
map.putAll( m );
} finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
map.clear();
} finally {
w.unlock();
}
}
public V get(Object key) {
r.lock();
try {
return map.get( key );
} finally {
r.unlock();
}
}
public int size() {
r.lock();
try {
return map.size();
} finally {
r.unlock();
}
}
public boolean isEmpty() {
r.lock();
try {
return map.isEmpty();
} finally {
r.unlock();
}
}
public boolean containsKey(Object key) {
r.lock();
try {
return map.containsKey( key );
} finally {
r.unlock();
}
}
public boolean containsValue(Object value) {
r.lock();
try {
return map.containsValue( value );
} finally {
r.unlock();
}
}
}
发表回复