JUC并发编程——各种锁的理解(基于狂神说的学习笔记)

各种锁的理解

公平锁与非公平锁

公平锁:非常公平,不能够插队,先来后到

非公平锁:可以插队,比较灵活(默认都是非公平,如:synchronized,lock)

// Lock lock = new ReentrantLock(); 不带参数的构造方法
public ReentrantLock() {
    sync = new NonfairSync();
}
// Lock lock = new ReentrantLock(true); 带参数的构造方法   
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

可重入锁(递归锁)

当一个锁中还有一个锁时,线程不会放弃第一把锁,就像递归一样层层深入,一直持有锁。

可以类比成:当你拿到进入一个房子的锁,进入房子后,再拿到进入卧室的锁,进入卧室,此时,就算你已经拿到了卧室的锁,你也并没有放弃房子的锁,别人依然进不来房子。

synchronized版

package lock;

// Synchronized
public class Demo01 {

    public static void main(String[] args) {
        phone phone = new phone();

        new Thread(()->{
            phone.sms();
        },"A").start();

        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

class phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"sms");
        call();
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"call");
    }
}

lock 版

package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {

    public static void main(String[] args) {
        phone2 phone2 = new phone2();

        new Thread(()->{
            phone2.sms();
        },"A").start();

        new Thread(()->{
            phone2.sms();
        },"B").start();
    }
}

class phone2{
    Lock lock = new ReentrantLock();
    public void sms(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"sms");
            call();// 这里也有锁
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }

    }

    public void call(){
        lock.lock();// 锁必须配对,否则会死锁

        try {
            System.out.println(Thread.currentThread().getName()+"call");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

运行结果如下:

JUC并发编程——各种锁的理解(基于狂神说的学习笔记)_第1张图片

从结果可以看到,即使A线程进入了call方法,也依然没有放弃sms方法的锁,B线程依然无法进入sms方法

自旋锁

一个自旋锁的源代码示例

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    return v;
}

自旋锁示例:

设计一个自旋锁:

package lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁
 */
public class Demo03 {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    // 加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"-->my lock");
        while(!atomicReference.compareAndSet(null,thread)){

        }
        System.out.println(Thread.currentThread().getName()+"--->has this lock");
    }

    // 解锁
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"-->my unlock");
        atomicReference.compareAndSet(thread,null);
    }
}

该自旋锁示例本质上使用了CAS操作,笔者在狂神说代码上加入了

System.out.println(Thread.currentThread().getName()+"--->has this lock");

测试类:

package lock;

import java.util.concurrent.TimeUnit;

public class TestDemo03 {
    public static void main(String[] args) throws InterruptedException {
        // 底层使用的是CAS
        Demo03 lock = new Demo03();

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                lock.myUnlock();
            }

        },"T1").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                lock.myUnlock();
            }
        },"T2").start();

    }

}

结果如下:

JUC并发编程——各种锁的理解(基于狂神说的学习笔记)_第2张图片

至此,读者可以看到,T1在打印“has this lock”之后,T2是无法持有该锁,它在不断地自旋,即进入while循环一直判断,我们来看一下这个代码在做什么

while(!atomicReference.compareAndSet(null,thread))

这条代码是actomicReference调用了一个compareAndSet方法(CAS),这个方法是用来判断,如果Thread为Null,则加入修改为thread,并返回true,!true == false,则会退出while循环,如果Thread为某个线程thread,则于CAS的预期值(null)不同,会返回false,!false == true,所以会进入循环,循环体内什么都没有,又会进入判断,周而复始,直到那个线程unlock,调用CAS将thread修改为null。

思考:为什么要让锁一直自旋判断呢?

你可能感兴趣的:(JUC并发编程,学习,笔记,java)