美文网首页
Java 面试系列:Java 中的各种锁和 CAS + 面试题

Java 面试系列:Java 中的各种锁和 CAS + 面试题

作者: you的日常 | 来源:发表于2021-02-06 16:57 被阅读0次

    如果说快速理解多线程有什么捷径的话,那本文介绍的各种锁无疑是其中之一,它不但为我们开发多线程程序提供理论支持,还是面试中经常被问到的核心面试题之一。因此下面就让我们一起深入地学习一下这些锁吧。

    乐观锁和悲观锁

    悲观锁和乐观锁并不是某个具体的“锁”而是一种并发编程的基本概念。乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入。

    悲观锁

    悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观地认为,不加锁的并发操作一定会出问题。

    乐观锁

    乐观锁正好和悲观锁相反,它获取数据的时候,并不担心数据被修改,每次获取数据的时候也不会加锁,只是在更新数据的时候,通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作,如果没被其他线程修改则进行数据更新,如果被其他线程修改则不进行数据更新。

    公平锁和非公平锁

    根据线程获取锁的抢占机制,锁又可以分为公平锁和非公平锁。

    公平锁

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

    非公平锁

    非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。
    ReentrantLock 提供了公平锁和非公平锁的实现。

    公平锁:new ReentrantLock(true)
    非公平锁:new ReentrantLock(false)
    如果构造函数不传任何参数的时候,默认提供的是非公平锁。

    独占锁和共享锁

    根据锁能否被多个线程持有,可以把锁分为独占锁和共享锁。

    独占锁

    独占锁是指任何时候都只有一个线程能执行资源操作。

    共享锁

    共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。比如 Java 中的 ReentrantReadWriteLock 就是共享锁的实现方式,它允许一个线程进行写操作,允许多个线程读操作。

    ReentrantReadWriteLock 共享锁演示代码如下:

    public class ReadWriteLockTest {
        public static void main(String[] args) throws InterruptedException {
            final MyReadWriteLock rwLock = new MyReadWriteLock();
            // 创建读锁 r1 和 r2
            Thread r1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    rwLock.read();
                }
            }, "r1");
            Thread r2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    rwLock.read();
                }
            }, "r2");
            r1.start();
            r2.start();
            // 等待同时读取线程执行完成
            r1.join();
            r2.join();
            // 开启写锁的操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    rwLock.write();
                }
            }, "w1").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    rwLock.write();
                }
            }, "w2").start();
        }
        static class MyReadWriteLock {
            ReadWriteLock lock = new ReentrantReadWriteLock();
            public void read() {
                try {
                    lock.readLock().lock();
                    System.out.println("读操作,进入 | 线程:" + Thread.currentThread().getName());
                    Thread.sleep(3000);
                    System.out.println("读操作,退出 | 线程:" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.readLock().unlock();
                }
            }
            public void write() {
                try {
                    lock.writeLock().lock();
                    System.out.println("写操作,进入 | 线程:" + Thread.currentThread().getName());
                    Thread.sleep(3000);
                    System.out.println("写操作,退出 | 线程:" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.writeLock().unlock();
                }
            }
        }
    }
    

    以上程序执行结果如下:

    读操作,进入 | 线程:r1
    
    读操作,进入 | 线程:r2
    
    读操作,退出 | 线程:r1
    
    读操作,退出 | 线程:r2
    
    写操作,进入 | 线程:w1
    
    写操作,退出 | 线程:w1
    
    写操作,进入 | 线程:w2
    
    写操作,退出 | 线程:w2
    

    相关文章

      网友评论

          本文标题:Java 面试系列:Java 中的各种锁和 CAS + 面试题

          本文链接:https://www.haomeiwen.com/subject/xtvebktx.html