您的位置:首页 > 其它

锁的公平与非公平

2017-06-10 21:29 253 查看

  锁是给线程用的,线程拿到锁之后才能干活。当多个线程竞争一个锁时,同一个时间只能有一个线程脱颖而出持有锁,其他线程等该线程释放锁后发起下一轮竞争。那么这种竞争就存在公平性问题,如果是公平的竞争,那么这些线程就得有序来依次得到锁,这就需要线程们请求的按先来后到排队,第一个线程使用完后把锁递给第二个线程,以此类推。非公平的锁是无序的,锁在被释放那会儿刚好谁运气好碰到了就给谁。

  举个例子:线程甲、乙、丙依次请求锁,如果是公平的,那么就按这个次序来。比如乙先取到了锁,那么没有意外,等它释放后肯定是先甲后丙来取得锁;如果是非公平,甲先持有锁,乙跑过来争,发现被甲拿了就去睡觉(被挂起)了,当甲释放锁通知乙来取的过程中,刚好丙半路杀过来了,把锁拿去用了,等丙释放锁时乙刚好完全被唤醒拿到了锁。

  从上面例子可以看到,如果竞争很激烈,锁被持有时间短,那么非公平锁能充分利用时间,公平锁反而因为线程的切换浪费了时间。反之,如果线程持有锁的时间长,那么非公平锁会被频繁请求,线程重试次数多,做了很多无用功,而公平锁按部就班传递锁反而减少了不必要的线程调度。内置锁(synchronized)只能是非公平的,显式锁(ReentrantLock)可以自己定义,下面看代码:

package com.wulinfeng.concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FairAndUnfairLock {

private Lock lock; // 被线程们竞争的一把锁,而且多个线程争的都是这同一把

public FairAndUnfairLock(boolean isFair) {
this.lock = new ReentrantLock(isFair);
}

/**
* 根据是否公平设置锁,线程们进入到这个方法,说明都是来争锁的,某一线程争到了,其他线程就得等
*
* @param isFair
*/
public void fightForLock() {
lock.lock();
try {
System.out.println("----" + Thread.currentThread().getName() + "获得锁.");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}

public static void main(String[] args) {
final FairAndUnfairLock lock = new FairAndUnfairLock(true); // 公平锁
int threadNums = 10;
Thread[] threads = new Thread[threadNums];
for (int i = 0; i < threadNums; i++) {
threads[i] = new Thread(new Runnable() {

@Override
public void run() {
System.out.println("****" + Thread.currentThread().getName() + "请求锁.");
lock.fightForLock();
}
});
}

for (int i = 0; i < threadNums; i++) {
threads[i].start();
}
}
}

  输出结果:

****Thread-0请求锁.
----Thread-0获得锁.
****Thread-2请求锁.
****Thread-4请求锁.
****Thread-6请求锁.
****Thread-8请求锁.
****Thread-1请求锁.
****Thread-3请求锁.
****Thread-5请求锁.
****Thread-7请求锁.
****Thread-9请求锁.
----Thread-2获得锁.
----Thread-4获得锁.
----Thread-6获得锁.
----Thread-8获得锁.
----Thread-1获得锁.
----Thread-3获得锁.
----Thread-5获得锁.
----Thread-7获得锁.
----Thread-9获得锁.

  从上面可以看到,线程0请求锁后立即得到,而后面的线程2-4-6-8-1-3-5-7-9依次请求锁在排队,等0释放后他们还是按这个顺序得到了锁。再来看下非公平的,把true改成false,再把这段休眠的代码注掉

        try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

跑一把,可以看到对同一个锁竞争后获取锁顺序是乱的:

****Thread-0请求锁.
----Thread-0获得锁.
****Thread-1请求锁.
----Thread-1获得锁.
****Thread-6请求锁.
----Thread-6获得锁.
****Thread-4请求锁.
----Thread-4获得锁.
****Thread-2请求锁.
****Thread-8请求锁.
****Thread-3请求锁.
----Thread-8获得锁.
----Thread-3获得锁.
----Thread-2获得锁.
****Thread-5请求锁.
----Thread-5获得锁.
****Thread-7请求锁.
----Thread-7获得锁.
****Thread-9请求锁.
----Thread-9获得锁.

  上面锁被线程4获取后,2-8-3依次请求,4释放锁时本该被2获取,但2在被唤醒的过程中8刚好来了并取到了锁,8用完了2还是没完全醒过来,然后3又来了取走了锁,当3释放后锁才被完全醒过来的2拿到。

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: