*公平和非公平锁
-
公平锁是指多个线程按照申请锁的顺序来获取锁类似排队,先来后到
-
非公平锁是指,在高并发的情况下,有可能后面的线程比前面的线程先得到锁,多个线程获取锁的顺序并不按照申请锁的顺序,这就可能造成优先级反转或者饥饿现象
*两者的区别
并发包ReentrantLock的创建,可以指定构造函数的boolean类型来得到公平锁或者非公平锁,默认是非公平锁
公平锁,就是很公平,在并发环境下,每个线程在获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,
就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己
非公平锁,比较粗鲁,上来就尝试直接占有锁,如果尝试失败,就再采用类似公平锁那种方式。
题外话
-
对于ReentrantLock而言,通过构造函数指定该锁是否为公平锁,默认是false,非公平锁,非公平锁的优点在于吞吐量比公平锁大.
-
synchronized也是一种非公平锁
*可重入锁
可重入锁也叫做递归锁,指的是同一线程,外层函数获得锁之后,内层递归函数仍然能获取该锁的代码。
ReentrantLock/synchronized就是典型的可重入锁,可重入锁最大的作用就是避免死锁
使用synchronized实现
public class ReentrantLockDemo{
public static void main(String[] args){
Phone p = new Phone();
new Thread(() -> {
p.sendEms();
},"t1").start();
new Thread(() -> {
p.sendEms();
},"t2").start();
}
}
class Phone{
public synchronized void sendEms(){
System.out.println(Thread.currentThread().getName() + "\tsendEms");
sendEmail();
}
public synchronized void sendEmail(){
System.out.println(Thread.currentThread().getName() + "\tsendEmail");
}
}
使用RenentrantLock实现
public class ReentrantLockDemo{
public static void main(String[] args){
Phone p = new Phone();
Thread t3 = new Thread(p,"t3");
Thread t4 = new Thread(p,"t4");
t3.start();
t4.start();
}
}
class Phone implements Runnable{
Lock l = new ReentrantLock();
@Override
public void run(){
get();
}
public void get(){
l.lock();
try{
System.out.println(Thread.currentThread().getName() + "\tget");
set();
}finally{
l.unlock();
}
}
public void set(){
l.lock();
try{
System.out.println(Thread.currentThread().getName() + "\tset");
}finally{
l.unlock();
}
}
}
*自旋锁
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
*手写一个自旋锁
public class SpinLockDemo {
public static void main(String[] args) {
SpinLock s = new SpinLock();
new Thread(() -> {
s.myLock();
// 模拟A线程占用锁5秒钟
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.myUnlock();
}, "A").start();
// 暂停1秒钟,保证A线程先执行
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
// B线程在A线程持有锁的这5秒期间,会一直循环访问comepareAndSet,
// 直到A线程解锁之后,B线程才会获得锁,然后1秒之后B线程解锁
s.myLock();
// 暂停1秒钟,使结果清晰
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.myUnlock();
}, "B").start();
}
}
class SpinLock {
AtomicReference<Thread> ar = new AtomicReference<>();
public void myLock() {
Thread thread = Thread.currentThread();
// 如果是第一个线程进来,则为其设置上锁,结果返回true,退出循环
// 如果不是第一个线程进来,则不会为其上锁,结果会返回false,一直循环
System.out.println(thread.getName() + "\t进来了");
while (!ar.compareAndSet(null, thread)) {
}
System.out.println(thread.getName() + "\t拿到锁了");
}
public void myUnlock() {
Thread thread = Thread.currentThread();
// 当前线程使用完锁之后,为其解锁
ar.compareAndSet(thread, null);
System.out.println(thread.getName() + "\t解锁了");
}
}