java并发
1. 线程的通信
wait()
使线程进入睡眠状态
notify()
随机唤醒一个等待的线程,它将获得一次抢夺锁的机会
notifyAll()
唤醒所有等待的线程
临界区: 需要线程同步的代码区
$$
运行状态
\dfrac{ wait() }{}
睡眠状态
\dfrac{ notify() }{}
等锁状态
\dfrac{ 获得锁 }{}
运行状态
$$
注意
- 3个方法只能在同步块里面被调用
- 使用
while
避免被假唤醒 - 不要使用全局对象,字符串常量作为锁
public class MywaitNotify{
MonitorObject monitor = new MonitorObject();
boolean wasSignalled = false;
public void doWait() {
synchronized(monitor){
while (!wasSignalled){ //用while代替if防止假唤醒
monitor.wait(); //wait(),notify(),notifyAll()必须位于同步块内
}
wasSignalled = false;
}
}
public void doNotify() {
synchronized(monitor){
wasSignalled = true;
monitor.notify(); //notity后并不意味着等待线程立即可以继续执行,而只是可以有机会再次抢夺锁
}
}
}
2. 饥饿与公平
- 若一个线程因为cpu时间全部被其他线程抢走而得不到cpu运行时间,这种状态称之为饥饿
- 公平锁的实现
public class FairLock {
private boolean locked; //锁区是否正处于锁定状态(锁区是否已有其他线程存在尚未离开)
private Thread lockingThread; //当前在锁区的那个线程
private List<LockObject> waittingThreads = new ArrayList<LockObject>();
public void lock() throws InterruptedException{
LockObject lockObject = new LockObject();
boolean isAllowedEnterLockedArea = false;
//线程开始排队,谁先进来,谁先被add进List,也就是排在最前面
synchronized (this) {
waittingThreads.add(lockObject);
}
//若不允许进入锁区,则自旋等待
while (!isAllowedEnterLockedArea) {
synchronized (this) {
/* 当前线程是否允许进入锁区的线程(isAllowedEnterLockedArea)?
* 如果锁区没有其他线程(locked=false) 且 排队排在最前面
* 则当前线程是被允许进入锁区的线程
*/
isAllowedEnterLockedArea = !locked && waittingThreads.get(0) == lockObject;
/* 若允许进入锁区,则在进入前将"锁区已有线程"的标志置为true,
* 并从排队队列中将自己移除 */
if (isAllowedEnterLockedArea) {
locked = true;
waittingThreads.remove(lockObject);
lockingThread = Thread.currentThread();
return;
}
}
try {
/* 不被允许进入锁区的线程(isAllowedEnterLockedArea=false)
* 使用他排队时使用的lockObject对象作为锁标志的锁睡眠了,
* 直到有其他线程调用这个lockObject对象的notify()叫醒他
* -----------------------
* 这里为什么不直接调用lockObject.wait()方法?
* --为了防止信号丢失!
* 因为自带的wait()或notify()方法是没有状态的,
* 有可能当前线程(线程A)执行到此处,在还没有进入睡眠时,
* 其他线程unlock,就已经notify了线程A,
* 这时由于线程A不是出于睡眠状态而忽略了notify的信号,
* 接着线程A进入睡眠, 因为唤醒它的信号已被忽略,
* 那么线程A将永远不会醒来
*/
lockObject.doWait();
} catch (InterruptedException e) {
/* 若因出现异常,在等待进入锁区的睡眠中被异常打断,
* 则将其从排队队列中踢出 */
synchronized (this) {
waittingThreads.remove(lockObject);
}
throw e;
}
}
}
public synchronized void unlock(){
if (this.lockingThread != Thread.currentThread()){
throw new IllegalMonitorStateException("当前线程并不是加锁者,解锁失败");
}
locked = false;
lockingThread = null;
//在离开锁区时,若还有其他线程在排队等待进入锁区,则叫醒排在最前面的那个线程
if (waittingThreads.size() > 0){
waittingThreads.get(0).doNotify();
}
}
}
public class LockObject {
private boolean isNotified; //使用成员变量记住信号,防止信号丢失(带状态的锁)
public synchronized void doWait() throws InterruptedException{
while(!isNotified){
this.wait();
}
isNotified = false;
}
public synchronized void doNotify(){
this.isNotified = true;
this.notify();
}
@Override
public boolean equals(Object o){
return this == o;
}
}
3. 嵌套管程锁死
有嵌套同步块A和B,锁分别是LockA,LockB,线程1在持有外层同步块A的锁LockA的情况下,进入同步块B,调用LockB.wait()进入睡眠,等待其他线程调用LockB.notify()将其唤醒, 至此,线程1就释放了LockB, 但仍持有LockA. 这时, 其他线程想进入锁同样为LockA的同步块去唤醒线程A(调用LockB.notify),却发现因线程1持有锁LockA睡眠了,无法获得该锁,从而导致死锁状态
简要的归纳就是:
线程带着其它锁没有释放的情况下进入了休眠(因同步块嵌套导致),导致其他线程无法获得这个锁去叫醒他
嵌套管程锁死的例子:
public class Lock{
private Monitor monitor = new Monitor();
private boolean locked;
public void lock(){
synchronized (this){
while (locked){
synchronized(monitor){
//进入休眠时,只释放了monitor锁,没有释放this锁
monitor.wait();
}
}
locked = true;
}
}
public void unlock(){
//因为this被占用, 无法进入同步块唤醒已睡眠的线程
synchronized(this){
locked = false;
synchronized(monitor){
monitor.notify();
}
}
}
}
死锁 与 嵌套管程锁死 的区别:
死锁中,两个线程都在等待对方释放锁.
嵌套管程锁死中,线程1持有锁A, 同事等待从线程2发来的信号,线程2需要锁A来发信号给线程1.
4. Slipped Conditions
从一个线程检查某一特定条件, 到操作此条件期间,这个条件已被其他线程所改变
public class Lock {
private boolean locked;
public void lock() {
synchronized (this){ //同步块1
while (locked){
this.wait();
}
}
/* 这里同步块1与同步块2间可能出现slipped conditions:
* 一个线程(线程A)检查到locked=false跳出同步块1后,即将准备进入同步块2,此时:
* this锁已被释放. 这时新的线程B调用lock()进入同步块1,检查到locked=false,
* 因此线程B马上跳出同步块1,进入同步块2,将locked置为true,
* 接下来线程A才进入同步块2,而此时发现:期间locked已被别人(线程B)修改过了
*/
synchronized(this){ //同步块2
locked = true;
}
}
public void unlocked() {
locked = false;
this.notify();
}
}
为避免slipped conditions, 条件的检查和设置必须是原子的, 也就是在第一个线程检查和设置条件期间,不会有其他线程检查这个条件
网友评论