Java公平锁与非公平锁
定义
当程序使用多线程里,难免有多线程争夺资源的情况。而资源只能被一个线程独占使用,至于资源怎么分配,就涉及到非公平锁与公平锁了。
非公平锁:非公平锁的资源的分配是随机的,看谁先抢到就给谁。可能会出现一个线程长期霸占资源,而另一线程长期得不到资源。
公平锁:顾名思义,公平锁的资源是公平分配的,先来先得,后来排队。
实例
开20个线程进行计数,每个线程计算到10000,最后
非公平锁之synchroized实现
使用synchroized锁的都是非公平锁。实现如下
import org.springframework.util.StopWatch;
public class ClassTestApp4lication {
public static Integer i = new Integer(0);
public static Object oj = new Object();
public static void main(String[] args) {
StopWatch watch = new StopWatch();
watch.start();
for (int ac = 0; ac < 20; ac++) {
Thread t1 = new Thread() {
@Override
public void run() {
for (int ac2 = 0; ac2 < 10000; ac2++) {
synchronized (oj) {
System.out.println("线程ID:"+Thread.currentThread().getId());//输出当前线程ID,便于确认分配的机制
i = i + 1;
}
}
}
};
t1.start();
}
}
}
因为输出的结果太长了,这里抽取部分输出结果,如下:
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:12
线程ID:14
线程ID:14
线程ID:14
线程ID:14
线程ID:14
线程ID:27
线程ID:27
线程ID:27
线程ID:27
非公平锁之ReentrantLock实现
用ReentrantLock实现,关键在于实例化ReentrantLock时传参为false。使用无参数重载也是非公平锁,如:
public static ReentrantLock rlock=new ReentrantLock(false)
或者 public static ReentrantLock rlock=new ReentrantLock()。
代码如下:
import org.springframework.util.StopWatch;
import java.util.concurrent.locks.ReentrantLock;
public class ClassTest3Application {
public static ReentrantLock rlock=new ReentrantLock(false);//false表示非公平锁,或使用无参数重载。
public volatile static int i;
public static void main(String[] args) {
StopWatch watch =new StopWatch();
watch.start();
for (int ac = 0; ac < 20; ac++) {
Thread t1 = new Thread() {
@Override
public void run() {
for (int ac2 = 0; ac2 < 1000; ac2++) {
try {
rlock.lock();
System.out.println("线程ID:"+Thread.currentThread().getId());
i = i + 1;
} finally {
rlock.unlock();
}
}
}
};
t1.start();
}
}
}
同样这里也只贴出部分的输出,如下
线程ID:12
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:17
线程ID:28
线程ID:28
公平锁 ReentrantLock实现
用ReentrantLock实现,关键在于实例化ReentrantLock时传参为true。如:
public static ReentrantLock rlock=new ReentrantLock(true)
import org.springframework.util.StopWatch;
import java.util.concurrent.locks.ReentrantLock;
public class ClassTest3Application {
public static ReentrantLock rlock=new ReentrantLock(true);//true 表示公平锁
public volatile static int i;
public static void main(String[] args) {
StopWatch watch =new StopWatch();
watch.start();
for (int ac = 0; ac < 20; ac++) {
Thread t1 = new Thread() {
@Override
public void run() {
for (int ac2 = 0; ac2 < 1000; ac2++) {
try {
rlock.lock();
System.out.println("线程ID:"+Thread.currentThread().getId());
i = i + 1;
} finally {
rlock.unlock();
}
}
}
};
t1.start();
}
}
}
同样这里也只贴出部分的输出,如下
线程ID:12
线程ID:13
线程ID:14
线程ID:15
线程ID:16
线程ID:17
线程ID:12
线程ID:18
线程ID:19
线程ID:13
线程ID:20
线程ID:14
线程ID:21
线程ID:15
线程ID:22
线程ID:16
线程ID:23
线程ID:17
线程ID:24
线程ID:12
线程ID:25
线程ID:18
线程ID:26
线程ID:19
性能测试
性能测试场景
开启20个线程,每个线程计数1000000次。计划耗时
测试结果
synchronized 非公平锁
测试序号 | 耗时(单位为秒) |
---|---|
1 | 0.5398486 |
2 | 0.5319575 |
3 | 0.5299667 |
4 | 0.5496109 |
5 | 0.5520412 |
ReentrantLock 非公平锁
测试序号 | 耗时(单位为秒) |
---|---|
1 | 0.4283422 |
2 | 0.4390329 |
3 | 0.4378793 |
4 | 0.4409746 |
5 | 0.4377871 |
ReentrantLock 公平锁
测试序号 | 耗时(单位为秒) |
---|---|
1 | 209.9848296 |
这个比较久,我等了好几分钟,就只做一次测试吧。
结论
性能 | 描述 | |
---|---|---|
synchronized 非公平锁 | 很好 | 耗时在0.54秒样子 |
ReentrantLock 非公平锁 | 最好 | 耗时在0.43秒样子 |
ReentrantLock 公平锁 | 最差 | 耗时超过200秒,与非公平锁不是一样等级,公平是有代价的。 |