公平锁和非公平锁
一、原因
1. CPU在调度线程的时候是从等待队列里随机挑选一个线程,由于这种随机性所以是无法保证线程先到先得的(synchronized控制的锁就是这种非公平锁),但这样就会产生饥饿现象,即有些线程(优先级较低的线程)可能永远也无法获取CPU的执行权,优先级高的线程会不断的强制它的资源。那么如何解决饥饿问题呢,这就需要公平锁了
- 产生饥饿的另一个原因是: 某个线程占据资源不释放,那其他需要该资源的线程只能处于无限等待中。在这里主要解决第一种饥饿问题
二、什么是公平锁
公平锁可以保证线程按照时间的先后顺序执行,避免饥饿现象的产生。但公平锁的效率比较低,因为要实现顺序执行,需要维护一个有序队列,即采用公平锁的时候,几乎不会出现同一线程连续执行多次
三、代码
// 代码1: 会发现:当采用公平锁的时候,线程不会出现同一线程连续执行多次;采用非公平锁的时候,会出现同一线程连续执行多次
package com.lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock2 {
// 公平锁
private static final ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁
private static final ReentrantLock nonFairLock = new ReentrantLock(false);
public static void main(String[] args) throws Exception{
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
fairLock.lock();
nonFairLock.lock();
System.out.println(Thread.currentThread().getName() + "获得了锁 缓存队列大小为:"
+ fairLock.getQueueLength());
} finally {
fairLock.unlock();
nonFairLock.unlock();
}
}
}
}, "test" + i).start();
}
}
}
// 代码2 结论见: http://blog.csdn.net/kjfcpua/article/details/8541433
package com.lock;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.System.out;
public class TestLock implements Runnable{
public enum LockType {
JVM, JUC
}
public static LockType lockType;
public static final long ITERATIONS = 5L * 100L * 100L;
public static long counter = 0L;
public static final Object jvmLock = new Object();
// 公平锁与非公平锁
public static final Lock jucLock = new ReentrantLock(true);
private static int numThreads;
private final long iterationLimit;
private final CyclicBarrier barrier;
private long localCounter = 0L;
public long getLocalCount() {
return this.localCounter;
}
public TestLock(final CyclicBarrier barrier, final long iterationLimit) {
this.iterationLimit = iterationLimit;
this.barrier = barrier;
}
public static void main(String[] args) throws Exception {
lockType = LockType.valueOf("JUC");
numThreads = 8;
final long start = System.nanoTime();
runTest(numThreads, ITERATIONS);
final long duration = System.nanoTime() - start;
out.printf("%d threads, duration %d (ns)\n", numThreads, duration);
out.printf("%d ns/op\n", duration / ITERATIONS);
out.printf("%d ops/s\n", (ITERATIONS * 1000000000L) / duration);
out.println("counter = " + counter);
}
private static void runTest(final int numThreads, final long iterationLimit) throws Exception{
CyclicBarrier barrier = new CyclicBarrier(numThreads);
Thread[] threads = new Thread[numThreads];
TestLock[] testLocks = new TestLock[numThreads];
for (int i = 0; i < threads.length; i++) {
testLocks[i] = new TestLock(barrier, iterationLimit);
threads[i] = new Thread(testLocks[i]);
}
for (Thread t : threads)
t.start();
for (Thread t : threads)
t.join();
for (int i = 0; i < threads.length; i++) {
out.printf("%d thread, local counter = %d\n", i, testLocks[i].getLocalCount());
}
}
@Override
public void run() {
try {
System.out.println(
"Number waiting = " + barrier.getNumberWaiting() + "; Parties = " + barrier.getParties());
// 同步屏障
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
switch (lockType) {
case JVM: jvmLockInc(); break;
case JUC: jucLockInc(); break;
}
}
private void jucLockInc() {
while (true) {
long count = 0L;
jucLock.lock();
try {
++counter;
count = counter;
} finally {
jucLock.unlock();
}
localCounter++;
if (count >= iterationLimit)
break;
}
}
private void jvmLockInc() {
while (true) {
long count = 0L;
synchronized (jvmLock) {
++counter;
count = counter;
}
localCounter++;
if (count >= iterationLimit)
break;
}
}
}
同步屏障
一、定义
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
通俗: 创建同步屏障时,会传递一个线程参数,当执行CyclicBarrier.await()方法时,只有当此时的await方法的线程数与传递的线程参数一致时,才会执行接下来的语句