Java SDK 并发包通过 Lock 和 Condition 两个接口来实现管程,其中 Lock 用于解决互斥问题,Condition 用于解决同步问题。
synchronized和Lock的区别
- 能够响应中断
- 支持超时
- 非阻塞地获取锁 ```java
// 支持中断的API void lockInterruptibly() throws InterruptedException; // 支持超时的API boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 支持非阻塞获取锁的API boolean tryLock();
而 synchronized 之所以能够保证可见性,也是因为有一条 synchronized 相关的规则:synchronized 的解锁 Happens-Before 于后续对这个锁的加锁
<a name="qn90y"></a>
### 什么是可重入锁
可重入锁就是同一个线程可以重复的获取一把锁 内部实现就是有一个计数器,每次加锁都会加一,减锁会减一,为0的时候就会释放锁。 可以避免死锁
<a name="BltES"></a>
### 公平锁与非公平锁
> 锁都对应着一个等待队列,如果一个线程没有获得锁,就会进入等待队列,当有线程释放锁的时候,就需要从等待队列中唤醒一个等待的线程。如果是公平锁,唤醒的策略就是谁等待的时间长,就唤醒谁,很公平;如果是非公平锁,则不提供这个公平保证,有可能等待时间短的线程反而先被唤醒。
🤔🤔🤔**队列是有序的?**<br />**
<a name="4myhg"></a>
### 用锁的最佳实践
> 永远只在更新对象的成员变量时加锁
> 永远只在访问可变的成员变量时加锁
> 永远不在调用其他对象的方法时加锁
> 减少锁的持有时间、减小锁的粒度
<br />
<a name="E5hb1"></a>
### 思考问题
以下代码是否会有死锁问题?
```java
class Account {
private int balance;
private final Lock lock
= new ReentrantLock();
// 转账
void transfer(Account tar, int amt){
while (true) {
if(this.lock.tryLock()) {
try {
if (tar.lock.tryLock()) {
try {
this.balance -= amt;
tar.balance += amt;
} finally {
tar.lock.unlock();
}
}//if
} finally {
this.lock.unlock();
}
}//if
}//while
}//transfer
}
会有活锁的问题
线程多的时候 可能会出现获取不到对方的锁,虽然不会阻塞但是会一直运行下去,解决方法就是获取锁增加随机时间lock.tryLock(随机数,NANOSECONDS)