zoukankan      html  css  js  c++  java
  • Java中的常见锁(公平和非公平锁、可重入锁和不可重入锁、自旋锁、独占锁和共享锁)

    公平和非公平锁

    • 公平锁:是指多个线程按照申请的顺序来获取值。在并发环境中,每一个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个就占有锁,否者就会加入到等待队列中,以后会按照 FIFO 的规则获取锁
    • 非公平锁:是指多个线程获取值的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。在并发环境中一上来就尝试占有锁,如果失败再进行排队,可能会造成优先级翻转或者饥饿现象
        // 常用的ReentrantLock无参构造默认是非公平锁
    
        /**
         * Creates an instance of {@code ReentrantLock}.
         * This is equivalent to using {@code ReentrantLock(false)}.
         */
        public ReentrantLock() {
            sync = new NonfairSync();
        }
    
        /**
         * Creates an instance of {@code ReentrantLock} with the
         * given fairness policy.
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }

    可重入锁和不可重入锁

    • 可重入锁:指的是同一个线程外层函数获得锁之后,内层仍然能获取到该锁,在同一个线程在外层方法获取锁的时候,在进入内层方法或会自动获取该锁 
    • 不可重入锁: 即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞
    /**
     * 可重入锁实现
     */
    public class ReentrantLock {
        boolean isLocked = false;
        Thread lockedBy = null;
        int lockedCount = 0;
        public synchronized void lock() throws InterruptedException {
            Thread thread = Thread.currentThread();
            while (isLocked && lockedBy != thread) {
                wait();
            }
            isLocked = true;
            lockedCount++;
            lockedBy = thread;
        }
        
        public synchronized void unlock() {
            if (Thread.currentThread() == lockedBy) {
                lockedCount--;
                if (lockedCount == 0) {
                    isLocked = false;
                    notify();
                }
            }
        }
    }
    
    /**
     * 测试类
     */
    public class Count {
        ReentrantLock lock = new ReentrantLock();
        public void print() throws InterruptedException{
            lock.lock();
            doAdd();
            lock.unlock();
        }
    
        private void doAdd() throws InterruptedException {
            lock.lock();
            // do something
            System.out.println("ReentrantLock");
            lock.unlock();
        }
    
        /**
         * 发现可以输出 ReentrantLock,我们设计两个线程调用 print() 方法,第一个线程调用 print() 方法获取锁,进入 lock() 方法,由于初始 lockedBy 是 null,所以不会进入 while 而挂起当前线程,而是是增量 lockedCount 并记录 lockBy 为第一个线程。接着第一个线程进入 doAdd() 方法,由于同一进程,所以不会进入 while 而挂起,接着增量 lockedCount,当第二个线程尝试lock,由于 isLocked=true,所以他不会获取该锁,直到第一个线程调用两次 unlock() 将 lockCount 递减为0,才将标记为 isLocked 设置为 false
         */    
        public static void main(String[] args) throws InterruptedException {
            Count count = new Count();
            count.print();
        }
    }
    /**
     * 不可重入锁实现
     */
    public class NotReentrantLock {
        private boolean isLocked = false;
        public synchronized void lock() throws InterruptedException {
            while (isLocked) {
                wait();
            }
            isLocked = true;
        }
        public synchronized void unlock() {
            isLocked = false;
            notify();
        }
    }
    
    /**
     * 测试
     */
    public class Count {
        NotReentrantLock lock = new NotReentrantLock();
        public void print() throws InterruptedException{
            lock.lock();
            doAdd();
            lock.unlock();
        }
    
        private void doAdd() throws InterruptedException {
            lock.lock();
            // do something
            lock.unlock();
        }
    
        /**
         * 当前线程执行print()方法首先获取lock,接下来执行doAdd()方法就无法执行doAdd()中的逻辑,必须先释放锁。这个例子很好的说明了不可重入锁
         */
        public static void main(String[] args) throws InterruptedException {
            Count count = new Count();
            count.print();
        }
    }

      synchronized 和 ReentrantLock 都是可重入锁

    自旋锁

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

    /**
     * 自选锁实现
     */
    public class SpinLock {
        private AtomicReference<Thread> atomicReference = new AtomicReference<>();
        private void lock () {
            System.out.println(Thread.currentThread() + " coming...");
            while (!atomicReference.compareAndSet(null, Thread.currentThread())) {
                // loop
            }
        }
    
        private void unlock() {
            Thread thread = Thread.currentThread();
            atomicReference.compareAndSet(thread, null);
            System.out.println(thread + " unlock...");
        }
    
        public static void main(String[] args) throws InterruptedException {
            SpinLock spinLock = new SpinLock();
            new Thread(() -> {
                spinLock.lock();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hahaha");
                spinLock.unlock();
    
            }).start();
    
            Thread.sleep(1);
    
            new Thread(() -> {
                spinLock.lock();
                System.out.println("hehehe");
                spinLock.unlock();
            }).start();
        }
    }
    
    /**
    * 输出内容:
    * Thread[Thread-0,5,main] coming... * Thread[Thread-1,5,main] coming... * hahaha * Thread[Thread-0,5,main] unlock... * hehehe * Thread[Thread-1,5,main] unlock... * * 获取锁的时候,如果原子引用为空就获取锁,不为空表示有人获取了锁,就循环等待,借鉴CAS底层实现 */

    独占锁(写锁)/共享锁(读锁)

    • 独占锁:指该锁一次只能被一个线程持有
    • 共享锁:该锁可以被多个线程持有

      对于 ReentrantLock 和 synchronized 都是独占锁;对于 ReentrantReadWriteLock 其读锁是共享锁而写锁是独占锁。读锁的共享可保证并发读是非常高效的,读写、写读和写写的过程是互斥的

    /**
     * 读写锁的应用
     */
    public class MyCache {
    
        private volatile Map<String, Object> map = new HashMap<>();
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        WriteLock writeLock = lock.writeLock();
        ReadLock readLock = lock.readLock();
    
        /**
         * 独占锁(写锁)
         */
        public void put(String key, Object value) {
            try {
                writeLock.lock();
                System.out.println(Thread.currentThread().getName() + " 正在写入...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                map.put(key, value);
                System.out.println(Thread.currentThread().getName() + " 写入完成,写入结果是 " + value);
            } finally {
                writeLock.unlock();
            }
        }
    
       /**
         * 共享锁(读锁)
         */
        public void get(String key) {
            try {
                readLock.lock();
                System.out.println(Thread.currentThread().getName() + " 正在读...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Object res = map.get(key);
                System.out.println(Thread.currentThread().getName() + " 读取完成,读取结果是 " + res);
            } finally {
                readLock.unlock();
            }
        }
    }
    
    /**
     * 测试代码
     */
    public class ReadWriteLockDemo {
        public static void main(String[] args) {
            MyCache cache = new MyCache();
    
            for (int i = 0; i < 5; i++) {
                final int temp = i;
                new Thread(() -> {
                    cache.put(temp + "", temp + "");
                }).start();
            }
    
            for (int i = 0; i < 5; i++) {
                final int temp = i;
                new Thread(() -> {
                    cache.get(temp + "");
                }).start();
            }
        }
    }
    执行结果:
    Thread-0 正在写入... Thread-0 写入完成,写入结果是 0 Thread-1 正在写入... Thread-1 写入完成,写入结果是 1 Thread-2 正在写入... Thread-2 写入完成,写入结果是 2 Thread-3 正在写入... Thread-3 写入完成,写入结果是 3 Thread-4 正在写入... Thread-4 写入完成,写入结果是 4 Thread-5 正在读... Thread-7 正在读... Thread-8 正在读... Thread-6 正在读... Thread-9 正在读... Thread-5 读取完成,读取结果是 0 Thread-7 读取完成,读取结果是 2 Thread-8 读取完成,读取结果是 3 Thread-6 读取完成,读取结果是 1 Thread-9 读取完成,读取结果是 4 能保证读写、写读和写写的过程是互斥的时候是独享的,读读的时候是共享的
  • 相关阅读:
    Dynamics AX 2012 R2 配置E-Mail模板
    Dynamics AX 2012 R2 设置E-Mail
    Dynamics AX 2012 R2 为运行失败的批处理任务设置预警
    Dynamics AX 2012 R2 耗尽用户
    Dynamics AX 2012 R2 创建一个专用的批处理服务器
    Dynamics AX 2012 R2 创建一个带有负载均衡的服务器集群
    Dynamics AX 2012 R2 安装额外的AOS
    Dynamics AX 2012 R2 将系统用户账号连接到工作人员记录
    Dynamics AX 2012 R2 从代码中调用SSRS Report
    Dynamics AX 2012 R2 IIS WebSite Unauthorized 401
  • 原文地址:https://www.cnblogs.com/ding-dang/p/13152559.html
Copyright © 2011-2022 走看看