java知识补充三:java锁有哪几种?公平/非公平,可重入,递归锁,自旋锁...

公平锁和非公平锁:

所谓公平锁就是各个线程争抢锁的时候要按照申请锁的顺序来,不能随意加塞,类似排队打饭 先来后到。

非公平锁则相反,谁先抢到锁,谁就先执行,没有抢到,才按照申请锁的顺序来执行。有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象。

公平锁/非公平锁的创建:
并发包ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或者非公平锁 默认是非公平锁。
如:

new ReentrantLock();
new ReentrantLock(fasle);
//都是非公平锁
new ReentrantLock(true);
//公平锁

就Java ReentrantLock而言,
通过构造函数指定该锁是否是公平锁 默认是非公平锁 非公平锁的优点在于吞吐量比公平锁大。
synchronized也是一种非公平锁.

可重入锁(递归锁):

可重入锁就是如果拿到锁进入A方法内,而A方法内又调用了另一个同步方法B,那么这个线程并不是拿到了两个同步方法的两把锁,而仅仅是刚开始拿到的一把锁。
即:同一线程外层函数获得锁之后,内层函数仍然能获取该锁的代码。在同一个线程方法外层获取锁的时候,在进入内层方法会自动获取该锁。

典型的可重入锁:
ReentrantLock和synchronized都是可重入锁。

可重入锁的作用:
最大的作用就是避免死锁。

class Phone{
    public synchronized void sendSms() throws Exception{
        System.out.println(Thread.currentThread().getName()+"\tsendSms");
        sendEmail();
    }
    public synchronized void sendEmail() throws Exception{
        System.out.println(Thread.currentThread().getName()+"\tsendEmail");
    }

}
public class ReenterLockDemo {
    /**
     * t1 sendSms
     * t1 sendEmail
     * t2 sendSms
     * t2 sendEmail
     * @param args
     */
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
    }
}

输出:

t1 sendSms
t1 sendEmail
t2 sendSms
t2 sendEmail

自旋锁:

自旋锁就是指获取锁的线程不会立即阻塞,而是通过循环判断的方式去获得锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗cpu。
直接举例,使用CAS的方法手写一个自旋锁:

public class SpinLockDemo {
	//常见线程的原子引用
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() ->{
            spinLockDemo.myLock();
            //等待5秒,便于其他线程进入并验证效果
            try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.MyUnLock();
        }, "AA").start();
		//保证AA先执行
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

		//BB直接执行
        new Thread(() ->{
            spinLockDemo.myLock();
            spinLockDemo.MyUnLock();
        }, "BB").start();

    }



    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+" come in");
        //如果不是null,在这里死循环,实现lock的效果
        while(!atomicReference.compareAndSet(null, thread)){

        }
    }

    public void MyUnLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName()+" invoked unlock");
    }
}

执行结果:
public class SpinLockDemo {
AtomicReference atomicReference = new AtomicReference<>();
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() ->{
spinLockDemo.myLock();
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.MyUnLock();
}, “AA”).start();

    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

    new Thread(() ->{
        spinLockDemo.myLock();
        spinLockDemo.MyUnLock();
    }, "BB").start();

}



public void myLock(){
    Thread thread = Thread.currentThread();
    System.out.println(Thread.currentThread().getName()+" come in");
    //如果不是null,在这里死循环,实现lock的效果
    while(!atomicReference.compareAndSet(null, thread)){

    }
}

public void MyUnLock(){
    Thread thread = Thread.currentThread();
    atomicReference.compareAndSet(thread, null);
    System.out.println(Thread.currentThread().getName()+" invoked unlock");
}

}

AA come in
BB come in
//下面一BB一定要等AA执行完毕才能执行完。
AA invoked unlock
BB invoked unlock

独占锁:

该锁一次只能被一个线程锁持有。ReentrantLock和synchronized都是独占锁。

共享锁:

指该锁可以被多个线程共享。
ReentrantLock的写是独占锁,但是读是共享的。可以提高并发的效率。

读写锁:

之前的juc笔记中有。

发布了16 篇原创文章 · 获赞 2 · 访问量 439

猜你喜欢

转载自blog.csdn.net/qq_31314141/article/details/104279804