多线程及并发面试基础(5)——Java中各种锁机制

1、公平锁/非公平锁

公平锁:
是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先到先得。
非公平锁:
是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象

举个例子:如果是公平锁,那么各个线程交替执行。非公平锁就是看哪个线程牛逼抢到的快准狠就它执行,不交替。看代码:

设置公平锁:

public class fairLockDemo {
    static Lock lock = new ReentrantLock(true);
    public static void  m(){
        for (int i = 1; i <=3; i++) {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(()->{ fairLockDemo.m();
        },"t1").start();
        new Thread(()->{
            fairLockDemo.m();
        },"t2").start();
        new Thread(()->{
            fairLockDemo.m();
        },"t3").start();
    }
}

输出结果: 三个线程轮流执行
公平锁执行结果

非公平锁:lock版本

Lock lock = new ReentrantLock(false); //这里false不填也可以,默认非公平

执行结果: 谁跑的快就有糖吃
lock版非公平锁
非公平锁:synchronized 版本

public class fairLockDemo {
    public static void  m(){
        for (int i = 1; i <=3; i++) {
            synchronized (fairLockDemo.class) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 1; i <=10 ; i++) {
            new Thread(()->{ fairLockDemo.m();
            },"t"+String.valueOf(i)).start();
        }
    }
}

执行结果:
syn非公平锁结果
总结:synchronized 非公平锁
ReentrantLock 默认非公平锁。也是相比syn的优势之一。

2、自旋锁(CAS底层原理)

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

代码:开启两个线程,对某个变量进行修改。t2 必须等到t1 先赋值之后置null之后才可以执行,否则while循环判断。

public class SpinLockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thead  = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t come in");
        //自旋锁比较
        while(!atomicReference.compareAndSet(null,thead)){}
    }

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

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            //睡一秒,让t2 自旋 判断
            System.out.println(Thread.currentThread().getName()+"\t 在执行睡1秒");
            try { TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.myUnLock();
        },"t1").start();

        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println(Thread.currentThread().getName()+"\t 循环判断好多好多鱼之后我终于可以执行了");
            spinLockDemo.myUnLock();
        },"t2").start();
    }
}

执行结果:
自旋锁demo结果

3、独占锁(写锁)/共享锁(读锁)/互斥锁

独占锁:指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁
共享锁:指该锁可以被多个线程所持有。

针对独占锁和共享锁使用业务比较单一,有没有什么锁可以在写的时候是写锁,而读到时候就是共享锁呢。很明显哪个魔鬼式式考虑到的,所以jdk8里面对ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁。

代码验证:写入的时候只允许一个写,读的时候可以多个读

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

    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"开始写入"+key);
            try { TimeUnit.MILLISECONDS.sleep(300); }catch (InterruptedException e) { e.printStackTrace(); }
            map.put(key,value);
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName()+"\t 写入完成");
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

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

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

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

运行结果:
ReentrantReadWriteLock的运行Demo结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值