笔记D《公平锁/非公平锁/可重入锁/递归锁/自旋锁》

本文详细解析了Java中的锁机制,包括公平锁与非公平锁的区别,可重入锁的实现原理,自旋锁的工作机制,以及独占锁与共享锁的概念。通过代码示例,展示了synchronized关键字和ReentrantLock类如何实现可重入锁,自旋锁的实现方式,以及ReentrantReadWriteLock如何区分读锁和写锁。

公平锁 & 非公平锁

  • 公平锁
    公平锁是指多个线程按照申请锁的顺序来获取,先来先得。

    在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列。如果为空,或者当前线程是等待队列的第一个,就占有锁,佛则就加入到等待队列中,按照 FIFO 的规则取出。

  • 非公平锁
    非公平锁是指在多线程获取锁的顺序并不是按照申请锁的顺序,而是一种抢占式的方式来获取锁。

synchronized,ReentrantLock 都是一种非公平锁。 但是ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或者非公平锁 默认是非公平锁。

可重入锁(递归锁)

指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码。在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

也就是说:线程可以进入到任何一个它已经拥有锁同步的代码块中。

作用:最大作用就是防止死锁,因为多层嵌套的锁,其实锁的是同一个对象,另一个含义就是:嵌套方法持有的是【同一把锁】。

  • 演示 synchronized 可重入锁
public class ReentrantLockDemo {
    private int x = 1;
    public static void main(String[] args) {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();

        for(int i = 0;i<30;i++) {
            new Thread(() -> {
                reentrantLockDemo.set();
            }, "t1").start();
        }
    }

    public synchronized void set(){ // 获取锁
        x = x+1;
        get(); 
    }
    public synchronized void get(){
        System.out.println(x);
    }
}
  • 演示 ReentrantLock 可重入锁
public class ReentrantLockDemo {

    public static void main(String[] args) {

        Phone phone = new Phone();
        Thread t3 = new Thread(phone);
        Thread t4 = new Thread(phone);
        t3.start();
        t4.start();
    }
}

class Phone implements Runnable{
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        get();
    }

    private void get() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t" + "get");
            set();
        }finally {
            lock.unlock();
        }
    }

    private void set() {
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName() + "\tset");
        }finally {
            lock.unlock();
        }
    }
}

自旋锁

自旋锁是指尝试获取锁的线程不会立即阻塞,而是 采用循环的方式尝试获取锁,这样的好处是可以减少线程上下文切换,但是自循环会导致消耗 CPU 资源。

public class SpinLockDemo { 
    // 原子对象
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "\t"+" come");

        while (!atomicReference.compareAndSet(null,thread)){
            //自旋
        }
    }

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

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

        new Thread(()->{
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"AA").start();

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

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

独占锁 & 共享锁

独占锁:锁每次只能被一个线程所持。synchronized,ReentrantLock 都是独占锁。

共享锁:锁可以被多个线程所有,ReentrantReadWriteLock 的【读锁是共享锁,写锁是独占锁。】

public class ReentrantLockDemo {
   public static void main(String[] args) {
       MyCache myCache = new MyCache();

       for(int i= 1; i<=5;i++){
           final int tmpInt = i;
           new Thread(()->{
               myCache.put(tmpInt+"",tmpInt+"");
           },i+"").start();
       }

       for(int i=1;i<=5;i++){
           final int tempInt = i;
           new Thread(()->{
               myCache.get(tempInt+"");
           },String.valueOf(i)).start();
       }
   }
}

class MyCache{
   private volatile Map<String,Object> map = new HashMap();
   private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

   public void put(String key,Object value){
       lock.writeLock().lock();

       System.out.println(Thread.currentThread().getName()+"\t"+"正在写入");

       try {
           TimeUnit.SECONDS.sleep(1);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       try {
           map.put(key, value);
           System.out.println(Thread.currentThread().getName() + "\t" + "写入完成");
       }finally {
           lock.writeLock().unlock();
       }
   }

   public void get(String key){
       lock.readLock().lock();
       System.out.println(Thread.currentThread().getName()+"\t 正在读取:"+key);
       try {
           TimeUnit.MILLISECONDS.sleep(300);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       try {
           Object result = map.get(key);
           System.out.println(Thread.currentThread().getName() + "\t 读取完成" + "\t" + result);
       }finally {
           lock.readLock().unlock();
       }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值