并发编程之公平锁与非公平锁
前言:准备记录一下并发编程的学习,做个笔记加深印象。并发编程学习的路神的视频
目录
一、问题
是否被阻塞的线程被唤醒的顺序不为乱序就是公平锁,反之则为非公平锁?
二、代码示例
2.1 synchronized实现的锁
public class TestSysn {
private static Logger logger = LoggerFactory.getLogger(TestSysn.class);
/**
* 定义一把锁
*/
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
//线程的数量
int N = 10;
Thread[] threads = new Thread[N];
for(int i = 0; i < N; ++i){
threads[i] = new Thread(() -> {
synchronized(lock){//t0 1.6 mutext---t0 t1....t9 到一个队列当中的去阻塞
logger.info(Thread.currentThread().getName() + " get synch lock!");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
//main 线程可以得到锁 持有了锁
synchronized(lock){
for(int i = 0; i < N; ++i){
//t0
threads[i].start();
Thread.sleep(200);
}
}
}
}
/**
---------------------输出结果-------------------
15:17:50.974 [Thread-9] INFO com.example.thread.TestSysn - Thread-9 get synch lock!
15:17:51.181 [Thread-8] INFO com.example.thread.TestSysn - Thread-8 get synch lock!
15:17:51.384 [Thread-7] INFO com.example.thread.TestSysn - Thread-7 get synch lock!
15:17:51.585 [Thread-6] INFO com.example.thread.TestSysn - Thread-6 get synch lock!
15:17:51.789 [Thread-5] INFO com.example.thread.TestSysn - Thread-5 get synch lock!
15:17:51.992 [Thread-4] INFO com.example.thread.TestSysn - Thread-4 get synch lock!
15:17:52.198 [Thread-3] INFO com.example.thread.TestSysn - Thread-3 get synch lock!
15:17:52.401 [Thread-2] INFO com.example.thread.TestSysn - Thread-2 get synch lock!
15:17:52.605 [Thread-1] INFO com.example.thread.TestSysn - Thread-1 get synch lock!
15:17:52.811 [Thread-0] INFO com.example.thread.TestSysn - Thread-0 get synch lock!
结果分析:多次执行打印结果都是倒序打印,synchronized实现的锁,如果有多个线程阻塞,最先阻塞的最后执行,最后阻塞的最先执行。
*/
2.2 lock实现的锁
public class TestLock {
private static Logger logger = LoggerFactory.getLogger(TestLock.class);
private static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
int N = 10;
Thread[] threads = new Thread[N];
for(int i = 0; i < N; ++i){
threads[i] = new Thread(() -> {
lock.lock();
logger.info(Thread.currentThread().getName() + " lock!");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
});
}
lock.lock();
for(int i = 0; i < N; ++i){
threads[i].start();
Thread.sleep(200);
}
lock.unlock();
}
}
/**
---------------------输出结果-------------------
15:31:52.454 [Thread-0] INFO com.example.thread.TestLock - Thread-0 lock!
15:31:52.481 [Thread-1] INFO com.example.thread.TestLock - Thread-1 lock!
15:31:52.505 [Thread-2] INFO com.example.thread.TestLock - Thread-2 lock!
15:31:52.529 [Thread-3] INFO com.example.thread.TestLock - Thread-3 lock!
15:31:52.555 [Thread-4] INFO com.example.thread.TestLock - Thread-4 lock!
15:31:52.580 [Thread-5] INFO com.example.thread.TestLock - Thread-5 lock!
15:31:52.601 [Thread-6] INFO com.example.thread.TestLock - Thread-6 lock!
15:31:52.622 [Thread-7] INFO com.example.thread.TestLock - Thread-7 lock!
15:31:52.646 [Thread-8] INFO com.example.thread.TestLock - Thread-8 lock!
15:31:52.671 [Thread-9] INFO com.example.thread.TestLock - Thread-9 lock!
结果分析:lock实现的同步如果有多个线程阻塞,唤醒的时候是按照阻塞顺序唤醒的。
*/
总结:回答最初提出的问题,阻塞线程被唤醒的顺序是乱序还是有序并不能说明,当前锁是公平的还是非公平的。
三、公平锁和非公平锁的加锁流程和区别
分析:非公平锁在lock调用加锁的时候去抢锁(公平锁调用lock不会上来就拿锁,而是去判断队列中是否有人在排队),如果加锁失败,则去看为什么失败(是否锁被别人持有),在判断的时候如果锁没有被别人持有,非公平锁就会直接加锁(不会判断是否有人在排队),成功则执行同步代码块,失败则排队park,在进入队列后判断自己的上一个节点是否是head,如果是head则自旋一次再次抢锁,反之则真正进入排队。
公平锁:第一次加锁的时候,他不会去尝试加锁,他会去看一下我的前面队列中是否还有人在排队,如果有人在排队,自己进入队列(并不是真正的排队),然后查看自己前面的人是否为head节点,如果是则继续拿锁,成功则进入同步代码块,失败则park排队。
synchronized是非公平锁,它在抢锁过程中底层自旋调用的是pthread_spin_lock方法,当其膨胀为重量级锁时底层调用 pthread_mutex_lock方法。
总之,对于非公平锁还是公平锁,都是一朝排队,永远排队。