公平锁与非公平锁、可重用锁、自旋锁、读写锁代码验证

一、公平锁与非公平锁

公平锁: 是指多个线程竞争同一资源时[等待同一个锁时],获取资源的顺序是按照申请锁的先后顺序的;公平锁保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁;
基本特点: 线程执行会严格按照顺序执行,等待锁的线程不会饿死,但整体效率相对比较低;

非公平锁: 是指多个线程竞争同一资源时,获取资源的顺序是不确定的,一般是抢占式的;非公平锁相对公平锁是增加了获取资源的不确定性,但是整体效率得以提升;
基本特点: 整体效率高,线程等待时间片具有不确定性;

ReentrantLock构造函数中可以直接传入一个boolean值fair,对公平性进行设置。当fair为true时,表示此锁是公平的,当fair为false时,表示此锁是非公平的锁;

ReentrantLock fairLock = new ReentrantLock(true);//公平的
ReentrantLock unFairLock = new ReentrantLock();//非公平的

二、可重用锁

可重用锁也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响
在JAVA中ReentrantLock 和synchronized 都是可重入锁;
ReenTrantLock原理:ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。

2.1 ReenterLockDemo

package com.zhang;

import java.util.concurrent.TimeUnit;

public class ReenterLockDemo {
    //synchronized和ReenterLock均为重用锁演示,其中synchronized是非公平锁,ReenterLock默认非公平锁
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendMS();
            }catch(Exception e){
                e.printStackTrace();
            }
        },"t1").start();

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

        new Thread(()->{
            try {
                phone.sendMS();
            }catch(Exception e){
                e.printStackTrace();
            }
        },"t2").start();
    }

    public static class Phone{
        public synchronized void sendMS(){
            System.out.println("当前线程名:"+Thread.currentThread().getName()+"\t 发送短信!");
            sendEmail();
        }

        public synchronized void sendEmail(){
            System.out.println("当前线程名:"+Thread.currentThread().getName()+"\t 发送邮件!");
        }
    }
}

测试结果:
在这里插入图片描述

ReentrantLock.Condition线程通信

Condition可以实现多路通知功能,也就是在一个Lock对象里可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,更加精确地进行调度,在调度线程上更加灵活,不再是笼统地申请同一把锁的等待线程全部启动。
ReentrantLock加锁的线程是通过Condition对象的await()、signal()/signalAll()方法来挂起当前线程(挂起前先调用lock.unlock()释放掉锁),唤醒相应线程(在某condition条件下等待的线程)先执行来实现的。


测试结果

2.2 ReentrantLock与synchronized简单对比

1.可重入性
ReentrantLock和synchronized都具有可重入性,写代码synchronized更简单,ReentrantLock需要将lock()和unlock()进行一一对应否则有死锁的风险;

2.锁的实现方式
Synchronized作为Java关键字是依赖于JVM实现的,而ReenTrantLock是JDK实现的,ReentrantLock则提供较为多样的实现方式和更多的功能;

3.公平性
ReentrantLock提供了公平锁和非公平锁两种API,开发人员完全可以根据应用场景选择锁的公平性;
synchronized是作为Java关键字是依赖于JVM实现,Java团队应该是优先考虑性能问题,因此synchronized是非公平锁。

三、自旋锁

是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
自旋锁的缺点
获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。
自旋锁的优点
自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

3.1 SpinLockDemo

package com.zhang;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

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();
        },"t1").start();

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

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

    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"\t 进来了,已添加锁!");
        while (!atomicReference.compareAndSet(null,thread)){
            //自旋操作
        }
    }

    public void myUnLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"\t 出去了,已释放锁!");
    }
}

测试结果:
在这里插入图片描述

四、读写锁

ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。

package com.zhang;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class MyCache{//资源类
    private volatile Map map= new HashMap();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    public void put(String key,Object value){//写操作
        try {
            rwLock.writeLock().lock();
            System.out.println("线程名:"+Thread.currentThread().getName() + "\t正在写入"+key);
            TimeUnit.MILLISECONDS.sleep(300);
            map.put(key,value);
            System.out.println("线程名:"+Thread.currentThread().getName() + "\t写入完成");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rwLock.writeLock().unlock();
        }
    }

    public void get(String key){//读操作
        try {
            rwLock.readLock().lock();
            System.out.println("线程名:"+Thread.currentThread().getName() + "\t正在读取"+key);
            TimeUnit.MILLISECONDS.sleep(300);
            Object result = map.get(key);
            System.out.println("线程名:"+Thread.currentThread().getName() + "\t读取完成,结果"+result);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rwLock.readLock().unlock();
        }
    }
}

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 0; i <3 ; i++) {
            final int tempInt = i;
            new Thread(()->{
                myCache.put(String.valueOf(tempInt),String.valueOf(tempInt));
            },String.valueOf(i)).start();
        }

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

测试结果:
在这里插入图片描述

参考文章
https://blog.youkuaiyun.com/black_bird_cn/article/details/81913671
https://www.jianshu.com/p/9d3660ad4358
https://blog.youkuaiyun.com/qq_19431333/article/details/70568478
https://www.cnblogs.com/xiaoxi/p/7651360.html
https://blog.youkuaiyun.com/sinat_32873711/article/details/106619981

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值