zoukankan      html  css  js  c++  java
  • Lock锁接口实现

    Lock锁接口实现

    学习材料来源于网络
    如有侵权,联系删除

    源码

    package java.util.concurrent.locks;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 与使用{@code sync}方法和语句相比,{@ code Lock}实现提供了更广泛的锁定
     * 操作。它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的{@link Condition}对象。
     *
     * 锁是用于控制多个线程对共享资源的访问的工具。通常,锁提供对共享资源的独占访问:
     * 一次只能有一个线程可以获取该锁,并且对共享资源的所有访问都需要首先获取该锁。
     * 但是,某些锁可能允许并发访问共享资源,例如{@link ReadWriteLock}的读取锁。
     *
     * <p>使用{@code Synchronized}方法或语句可
     * 访问与每个对象关联的隐式监视器锁,但是
     * 强制所有锁的获取和释放以块结构方式进行:
     * 当多个锁被使用时被获取的锁必须以相反的
     * 顺序释放,并且所有锁都必须在与被获取的锁相同的词法范围内释放。
     *
     * <p>虽然{@code sync}方法的作用域机制
     * 和语句使使用监视器锁的编程变得容易得多,并且
     * 并避免了很多常见的涉及锁的编程错误,但是
     * 在某些情况下您需要使用锁以更灵活的方式。例如,某些用于遍历并发访问的数据结构的算法需要使用“交接”或“链锁”:您
     * 取得节点A的锁,然后取得节点B,然后释放A并取得
     * C ,然后释放B并获取D,依此类推。 
     * {@code Lock}接口的实现通过
     * 允许在不同范围内获取和释放锁,并允许以任意
     * 顺序获取和释放多个锁,从而启用了此类技术。
     *
     * <p>随着灵活性的提高,额外的责任也随之增加。缺少块结构锁定将消除
     * {{code sync}方法和语句中发生的锁定的自动释放。在大多数情况下,应使用以下习惯用语
     * :
     *
     *  <pre> {@code
     * Lock l = ...;
     * l.lock();
     * try {
     *   // access the resource protected by this lock
     * } finally {
     *   l.unlock();
     * }}</pre>
     *
     *当锁定和解锁发生在不同的范围内时,
     *必须小心以确保通过try-finally或try-catch保护在保持锁的同时执行的所有代码,
     *以确保在必要时释放锁。
     *
     * <p> {@ code Lock}类还可以提供与隐式监视器锁完全不同的行为和语义,
     * 例如,保证顺序,不可重用或死锁。如果实现提供了这种特殊的语义
     *,那么实现必须记录这些语义。
     *
     * <p> {@ code Lock}类还可以提供与隐式监视器锁完全不同的行为和语义,
     * 例如,保证顺序,不可重用或死锁。如果实现提供了这种特殊的语义,
     * 那么实现必须记录这些语义。
     *
     * <p>请注意,{@ code Lock}实例只是普通对象,它们自身可以用作{@code Synchronized}语句中的目标。 
     * 获取{@code Lock}实例的监视器锁定与该实例的任何{@link #lock}方法都没有指定的关系
     *。 
     * 为避免混淆,建议您不要以这种方式使用{@code Lock} 
     * 实例,除非在它们自己的实现中使用。
     *
     * <p>除非另有说明,否则为任何
     * 参数传递{@code null}值将导致抛出{@link NullPointerException}。
     *
     * <h3>内存同步</h3>
     *
     * <p>所有{@code Lock}实现<em>必须</ em>强制执行与内置监视器锁相同的
     * 内存同步语义,如
     * <a href =“ https:// docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4“> 
     * Java语言规范(17.4内存模型)</a>:
     * <ul> 
     * <li>成功的{@code lock}操作具有与成功的<em> Lock </ em>操作相同的内存*同步效果。 
     * <li>成功的{@code unlock}操作与成功的<em> Unlock </ em>操作具有相同的内存同步效果。 
     * </ ul> 
     * 
     * 不成功的锁定和解锁操作,以及可重入的
     * 锁定/解锁操作,不需要任何内存*同步效果。
     *
     * <h3>实施注意事项s</h3>
     *
     * <p>锁获取的三种形式(可中断,
     * 不可中断和定时)在性能,特性,订购保证或其他实现质量上可能有所不同。
     * 此外,在给定的{@code Lock} 
     * 类中,中断<em> inginging </ em> *获取锁的功能可能不可用。
     * 因此,不需要实现为所有三种形式的锁获取定义完全相同的保证或语义,
     * 也不需要支持中断正在进行的锁获取。需要一个实现来清楚地*记录每个锁定方法所提供的语义和保证。
     * 在支持锁获取中断的程度上,它还必须遵守此接口中定义的中断语义:或者完全或仅在方法输入时才这样做。
     *
     * <p>由于中断通常意味着取消,并且对中断的检查通常是很少的,因此与正常方法返回相比,实现可能更喜欢对中断做出响应。即使可以
     * 表明在另一个操作之后发生的中断可能已经取消阻塞了线程,也是如此。实现应记录此行为。
     *
     * @see ReentrantLock
     * @see Condition
     * @see ReadWriteLock
     *
     * @since 1.5
     * @author Doug Lea
     */
    public interface Lock {
    
    	/ ** 
            *获取锁。 
            * 
            * <p>如果该锁不可用,则出于线程调度目的,当前线程将被禁用
            *,并且在获取该锁之前,该线程处于休眠状态。 
            * 
            * <p> <b>实现注意事项</ b> 
            * 
            * <p> {@ code Lock}实现可能能够检测对锁的错误使用,例如可能导致死锁的调用,并且
            * 在这种情况下抛出一个(未经检查的)异常。 *环境和异常类型必须由该{@code Lock}实现来记录。 
            * /
        void lock();
    
        /**
    	 *获取锁定,除非当前线程被{{@linkplain Thread#interrupt interrupted}破坏。 
    	 * 
    	 * <p>获取锁(如果有)并立即返回。 
    	 * 
    	 * <p>如果该锁不可用,则出于线程调度目的,当前线程将被禁用*并处于休眠状态,直到发生以下两种情况之一:
         *
    	 * <ul> 
    	 * <li>该锁由当前线程获取;或
    	 * <li>当前线程有一些其他线程{@linkplain Thread#interrupt interrupts},并且支持中断获取锁。 
    	 * </ ul>
         *
    	 * <p>如果当前线程:
    	 * <ul> 
    	 * <li>在进入此方法时已设置其中断状态;或
    	 * <li>在获取
    	 * 锁的过程中被{@linkplain Thread#interrupt interrupted中断},并且支持中断获取锁,
    	 * </ ul> *然后抛出{@link InterruptedException}并且当前线程的
    	 * 中断状态为已清除。
         *
    	 * <p> <b>实施注意事项</ b>
         *
    	 * <p>在某些*实现中可能无法中断锁获取,并且如果可能的话,可能是
    	 * 昂贵的操作。程序员应意识到可能是这种情况。在这种情况下,实现应记录在案。 
    	 * 
    	 * <p>与正常方法返回相比,实现可能更喜欢对中断做出响应。
    	 * 
    	 * <p> {@ code Lock}实现可能能够检测到*错误的使用锁,例如可能导致死锁的调用,
    	 * 并且在这种情况下可能引发(未经检查的)异常。环境和异常类型必须*由该{@code Lock}实现来记录。 
    	 * 
    	 * @throws InterruptedException如果当前线程在获取锁时被中断(并且支持锁获取的中断)
    	 */
        void lockInterruptibly() throws InterruptedException;
    
        /**
         * 仅在调用时释放锁时才获取锁。
         *
         * <p>获取该锁(如果有)并立即返回
         *,其值为{@code true}。
         * 如果锁不可用,则此方法将立即返回
         *,其值为{@code false}。
         *
         * <p>此方法的典型用法是:
         *  <pre> {@code
         * Lock lock = ...;
         * if (lock.tryLock()) {
         *   try {
         *     // 操纵保护状态
         *   } finally {
         *     lock.unlock();
         *   }
         * } else {
         *   // 执行替代动作
         * }}</pre>
         *
         * 此用法可确保在获取锁后将其解锁,并且
         *在未获取锁时不会尝试解锁。
         *
         * @return {@code true} 如果获得了锁,并且
         *         {@code false} 否则
         */
        boolean tryLock();
    
        /**
         *如果锁在给定的等待时间内是空闲的,并且
         *当前线程尚未{@linkplain Thread#interrupt interrupted},则获取该锁。
         *
         * <p>如果锁可用,则此方法立即返回
         *,其值为{@code true}。
         * 如果该锁不可用,则*当前线程将出于线程调度目的而被禁用
         * 并处于休眠状态,直到发生以下三种情况之一:
         * <ul>
         * <li>该锁是由当前线程获取的;要么
         * <li>当前线程的其他一些线程
         * {@linkplain Thread#interrupt interrupts},并支持锁定获取的中断;要么
         * <li>经过指定的等待时间
         * </ul>
         *
         * <p>如果获得了锁,则返回值{@code true}。
         *
         * <p>如果当前线程:
         * <ul>
         * <li>在进入此方法时已设置其中断状态;要么
         * <li>在获取*锁的过程中被{@linkplain Thread#interrupt interrupted中断了,并且支持中断获取锁,
         * </ul>
         * 然后抛出{@link InterruptedException}并清除当前线程的
         * 中断状态。
         *
         * <p>如果经过了指定的等待时间,则返回值{@code false}
         *。
         *如果时间*小于或等于零,则该方法将完全不等待。
         *
         * <p><b>实施注意事项</b>
         *
         * <p>在某些实现中,中断锁获取的能力*可能是不可能的,并且如果可能的话
         * 是一项昂贵的操作。 *程序员应意识到可能是这种情况。在这种情况下,
         * 实现应记录在案。
         *
         * <p>与正常方法返回或报告超时相比,实现可能更喜欢响应中断。
         *
         * <p> {@ code Lock}实现可以检测*错误使用锁,例如可能导致死锁的调用,并且在这种情况下可能引发(未经检查的)异常。
         * 必须通过该{@code Lock}实现来记录情况和异常类型。
         *
         * @param time等待锁的最长时间
         * @param {@code time}参数的时间单位
         * @return {@code true},如果已获得锁,则{@code false}
         *         获取锁之前是否经过了等待时间
         *
         * @throws InterruptedException-如果当前线程在获取锁时被中断
         *(并且支持锁的中断
         * 支持获取)
         */
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
        /**
         * 释放锁。
         *
         * <p><b>实施注意事项</b>
         *
         * <p>{@code Lock}实现通常会对可以释放锁的线程施加
         *限制(通常只有
         *
         *锁的持有者可以释放该锁),并且如果违反该限制,则可能引发
         *(未经检查)异常。
         *任何限制和例外
         *类型必须由该{@code Lock}实现来记录。
         */
        void unlock();
    
        /**
         *返回绑定到该
         * {@code Lock}实例的新{@link Condition}实例。
         *
         * <p>在等待条件之前,锁必须由当前线程持有。
         *对{@link Condition#await()}的调用将在等待之前自动释放锁
         *,并在等待返回之前重新获取该锁。
         *
         * <p><b>实施注意事项</b>
         *
         * <p> {@ link Condition}实例的确切操作取决于{@code Lock}实现,并且必须由该实现记录下来。
         *
         * @return 此{@code Lock}实例的新{@link Condition}实例
         * @throws UnsupportedOperationException如果此{@code Lock}
         *         实施不支持条件
         */
        Condition newCondition();
    }
    
    

    核心API

    方法 描述
    lock 获取锁的方法,若被其他线程占用,则会等待(阻塞)
    lockInterruptibly 在锁的获取过程中可以中断当前线程
    tryLock 尝试非阻塞地获取锁,立即返回
    unlocj 释放锁

    提示:根据Lock接口的源码注释,Lock接口的实现,具备和同步关键字同样的内存语义。

    ReentrantLock

    独享锁;支持公平锁、非公平锁两种模式;可重入

    示例1

    // 3、 演示可重入
    public class ReentrantDemo1 {
        private static final ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) {
            //第一次加锁
            lock.lock();
            try {
                System.out.println("第一次获取锁");
                System.out.println("当前线程获取锁的次数" + lock.getHoldCount());
                //第二次加锁
                lock.lock();
                System.out.println("第二次获取锁了");
                System.out.println("当前线程获取锁的次数" + lock.getHoldCount());
            }finally {
                //释放第二次锁
                lock.unlock();
                //释放第一次锁
                lock.unlock();
            }
            System.out.println("当前线程获取锁的次数" + lock.getHoldCount());
    
            // 如果不释放,此时其他线程是拿不到锁的
            new Thread(() -> {
                System.out.println(Thread.currentThread() + " 期望抢到锁");
                lock.lock();
                System.out.println(Thread.currentThread() + " 线程拿到了锁");
            }).start();
    
    
        }
    }
    

    正确的停止等待锁的线程

    示例2

    package icu.shaoyayu.multithreading.chapter4;
    
    import java.util.ArrayList;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author shaoyayu
     * @E_Mail
     * @Version 1.0.0
     * @readme :
     */
    // ReentrantLock 可重入锁示例
    public class ReentrantLockDemo1 {
        private Lock lock = new ReentrantLock();
    
        public static void main(String[] args) throws InterruptedException {
            ReentrantLockDemo1 demo1 = new ReentrantLockDemo1();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        demo1.test(Thread.currentThread());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
            thread1.start();
            Thread.sleep(500); // 等待0.5秒,让thread1先执行
    
            thread2.start();
            Thread.sleep(2000); // 两秒后,中断thread2
    
            thread2.interrupt();
        }
    
        public void test(Thread thread) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + ", 想获取锁");
            lock.lock();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
            try {
                System.out.println(thread.getName() + "得到了锁");
                Thread.sleep(10000); // 抢到锁,10秒不释放
            } finally {
                System.out.println(Thread.currentThread().getName() + "执行finally");
                lock.unlock();
                System.out.println(thread.getName() + "释放了锁");
            }
        }
    }
    

    结果:

    Thread-0, 想获取锁
    Thread-0得到了锁
    Thread-1, 想获取锁
    Thread-0执行finally
    Thread-0释放了锁
    Thread-1得到了锁
    Thread-1执行finally
    Thread-1释放了锁
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at icu.shaoyayu.multithreading.chapter4.ReentrantLockDemo1.test(ReentrantLockDemo1.java:46)
    	at icu.shaoyayu.multithreading.chapter4.ReentrantLockDemo1$1.run(ReentrantLockDemo1.java:24)
    	at java.lang.Thread.run(Thread.java:748)
    

    对代码进行修改

    // 可响应中断
    public class LockInterruptiblyDemo1 {
        private Lock lock = new ReentrantLock();
    
        public static void main(String[] args) throws InterruptedException {
            LockInterruptiblyDemo1 demo1 = new LockInterruptiblyDemo1();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        demo1.test(Thread.currentThread());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
            thread1.start();
            Thread.sleep(500); // 等待0.5秒,让thread1先执行
    
            thread2.start();
            Thread.sleep(2000); // 两秒后,中断thread2
    
            thread2.interrupt();
        }
    
        public void test(Thread thread) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + ", 想获取锁");
            lock.lockInterruptibly();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
            try {
                System.out.println(thread.getName() + "得到了锁");
                Thread.sleep(10000); // 抢到锁,10秒不释放
            } finally {
                System.out.println(Thread.currentThread().getName() + "执行finally");
                lock.unlock();
                System.out.println(thread.getName() + "释放了锁");
            }
        }
    }
    

    运行结果:

    Thread-0, 想获取锁
    Thread-0得到了锁
    Thread-1, 想获取锁
    java.lang.InterruptedException
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
    	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    	at icu.shaoyayu.multithreading.chapter4.LockInterruptiblyDemo1.test(LockInterruptiblyDemo1.java:42)
    	at icu.shaoyayu.multithreading.chapter4.LockInterruptiblyDemo1$1.run(LockInterruptiblyDemo1.java:23)
    	at java.lang.Thread.run(Thread.java:748)
    Thread-0执行finally
    Thread-0释放了锁
    

    ReadWriteLock

    维护一对关联锁,一个用于只读操作,一个用于写入;读锁可以由多个读线程同时持有,写锁是排他的。

    适合读取线程比写入线程多的场景,改进互斥锁的性能,示例场景:缓存组件、集合的并发线程安全性改造。

    锁降级指的是写锁降级成为读锁。把持住当前拥有的写锁的同时,再获取到读锁,随后释放写锁的过程。

    写锁是线程独占,读锁是共享,所以写->读是升级。(读->写,是不能实现的)

    示例3

    // 将hashmap改造一个并发安全的
    // 比hashTable的实现,效率高,读取的适合并不会同步执行
    public class MapDemo {
        private final Map<String, Object> m = new HashMap<>();
        private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        private final Lock r = rwl.readLock();
        private final Lock w = rwl.writeLock();
    
        public Object get(String key) {
            r.lock(); // 可以同时多个线程获取这把锁
            try {
                return m.get(key);
            } finally {
                r.unlock();
            }
        }
    
        public Object[] allKeys() {
            r.lock();
            try {
                return m.keySet().toArray();
            } finally {
                r.unlock();
            }
        }
    
        public Object put(String key, Object value) {
            w.lock(); // 一个线程获取 这把锁
            try {
                return m.put(key, value);
            } finally {
                w.unlock();
            }
        }
    
        public void clear() {
            w.lock();
            try {
                m.clear();
            } finally {
                w.unlock();
            }
        }
    }
    

    对于Hashtable而言

    Hashtable源码:

    package java.util;
    
    public class Hashtable<K,V>
        extends Dictionary<K,V>
        implements Map<K,V>, Cloneable, java.io.Serializable {
        private transient Entry<?,?>[] table;
        private transient int count;
        private int threshold;
        private float loadFactor;
        private transient int modCount = 0;
        public Hashtable(int initialCapacity, float loadFactor) {
            //
        }
        public Hashtable(int initialCapacity) {
            this(initialCapacity, 0.75f);
        }
        public Hashtable() {
            //
        }
        public Hashtable(Map<? extends K, ? extends V> t) {
            //
        }
        public synchronized int size() {
            //
        }
        public synchronized boolean isEmpty() {
            //
        }
        public synchronized Enumeration<K> keys() {
            //
        }
        public synchronized Enumeration<V> elements() {
            //
        }
        public synchronized boolean contains(Object value) {
            //
        }
        public boolean containsValue(Object value) {
            //
        }
        public synchronized boolean containsKey(Object key) {
    		//
        }
        @SuppressWarnings("unchecked")
        public synchronized V get(Object key) {
            //
        }
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        @SuppressWarnings("unchecked")
        protected void rehash() {
            //
        }
        private void addEntry(int hash, K key, V value, int index) {
            //
        }
        public synchronized V put(K key, V value) {
           //
        }
        public synchronized V remove(Object key) {
            //
        }
        public synchronized void putAll(Map<? extends K, ? extends V> t) {
           //
        }
        public synchronized void clear() {
            //
        }
        public synchronized Object clone() {
           //
        }
        public synchronized String toString() {
            //
        }
        private <T> Enumeration<T> getEnumeration(int type) {
            //
        }
        private <T> Iterator<T> getIterator(int type) {
            //
        }
        private transient volatile Set<K> keySet;
        private transient volatile Set<Map.Entry<K,V>> entrySet;
        private transient volatile Collection<V> values;
        public Set<K> keySet() {
            //
        }
        private class KeySet extends AbstractSet<K> {
            //
        }
        public Set<Map.Entry<K,V>> entrySet() {
           //
        }
        private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
            //
        }
        public Collection<V> values() {
            //
        }
        private class ValueCollection extends AbstractCollection<V> {
            //
        }
        public synchronized boolean equals(Object o) {
            //
        }
        public synchronized int hashCode() {
            //
        }
        @Override
        public synchronized V getOrDefault(Object key, V defaultValue) {
            //
        }
        @SuppressWarnings("unchecked")
        @Override
        public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
            //
        }
        @SuppressWarnings("unchecked")
        @Override
        public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            //
        }
        @Override
        public synchronized V putIfAbsent(K key, V value) {
            //
        }
        @Override
        public synchronized boolean remove(Object key, Object value) {
            //
        }
        @Override
        public synchronized boolean replace(K key, V oldValue, V newValue) {
            //
        }
        @Override
        public synchronized V replace(K key, V value) {
           //
        }
        @Override
        public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
            //
        }
        @Override
        public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            //
        }
        @Override
        public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            //
        }
        @Override
        public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            //
        }
        private void writeObject(java.io.ObjectOutputStream s)
            //
           //
        }
        private void readObject(java.io.ObjectInputStream s)
             throws IOException, ClassNotFoundException
        {
            //
        }
        private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
            throws StreamCorruptedException
        {
            //
        }
        private static class Entry<K,V> implements Map.Entry<K,V> {
            //
        }
        private static final int KEYS = 0;
        private static final int VALUES = 1;
        private static final int ENTRIES = 2;
        private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
            Entry<?,?>[] table = Hashtable.this.table;
            int index = table.length;
            Entry<?,?> entry;
            Entry<?,?> lastReturned;
            int type;
            boolean iterator;
            protected int expectedModCount = modCount;
            Enumerator(int type, boolean iterator) {
                this.type = type;
                this.iterator = iterator;
            }
            public boolean hasMoreElements() {
                //
            }
            @SuppressWarnings("unchecked")
            public T nextElement() {
               //
            }
            public boolean hasNext() {
                //
            }
            public T next() {
                //
            }
    
            public void remove() {
               //
                synchronized(Hashtable.this) {
                    //
                }
            }
        }
    }
    

    可以看到大部分的实现的都是对方法进行上锁来解决线程的高并发

    示例4

    读写高并发

    使用synchronized关键字

    // 不用读写锁
    public class ReentrantReadWriteLockDemo1 {
        public static void main(String[] args)  {
            final ReentrantReadWriteLockDemo1 readWriteLockDemo1 = new ReentrantReadWriteLockDemo1();
            // 多线程同时读/写
            new Thread(() -> {
                readWriteLockDemo1.read(Thread.currentThread());
            }).start();
    
            new Thread(() -> {
                readWriteLockDemo1.write(Thread.currentThread());
            }).start();
    
            new Thread(() -> {
                readWriteLockDemo1.read(Thread.currentThread());
            }).start();
        }
    
        // 不管读写,只有一个线程能用, 独享锁
        public synchronized void read(Thread thread) { // 2秒
            long start = System.currentTimeMillis();
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"正在进行“读”操作");
            }
            System.out.println(thread.getName()+"“读”操作完毕");
        }
    
        /** 写 */
        public synchronized void write(Thread thread) {
            long start = System.currentTimeMillis();
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"正在进行“写”操作");
            }
            System.out.println(thread.getName()+"“写”操作完毕");
        }
    }
    

    使用ReentrantReadWriteLock锁实现

    package icu.shaoyayu.multithreading.chapter4;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * @author shaoyayu
     * @E_Mail
     * @Version 1.0.0
     * @readme :
     */
    // 读写锁(既保证了读数据的效率,也保证数据的一致性)
    public class ReentrantReadWriteLockDemo2 {
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public static void main(String[] args) {
            final ReentrantReadWriteLockDemo2 readWriteLockDemo2 = new ReentrantReadWriteLockDemo2();
            // 多线程同时读/写
            new Thread(() -> {
                readWriteLockDemo2.read(Thread.currentThread());
            }).start();
    
            new Thread(() -> {
                readWriteLockDemo2.read(Thread.currentThread());
            }).start();
    
            new Thread(() -> {
                readWriteLockDemo2.write(Thread.currentThread());
            }).start();
        }
    
        // 多线程读,共享锁
        public void read(Thread thread) {
            readWriteLock.readLock().lock();
            try {
                long start = System.currentTimeMillis();
                while (System.currentTimeMillis() - start <= 1) {
                    System.out.println(thread.getName() + "正在进行“读”操作");
                }
                System.out.println(thread.getName() + "“读”操作完毕");
            } finally {
                readWriteLock.readLock().unlock();
            }
        }
    
        /**
         * 写
         */
        public void write(Thread thread) {
            readWriteLock.writeLock().lock();
            try {
                long start = System.currentTimeMillis();
                while (System.currentTimeMillis() - start <= 1) {
                    System.out.println(thread.getName() + "正在进行“写”操作");
                }
                System.out.println(thread.getName() + "“写”操作完毕");
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
    }
    

    示例5

    缓存示例

    // 缓存示例
    public class CacheDataDemo {
        // 创建一个map用于缓存
        private Map<String, Object> map = new HashMap<>();
        private static ReadWriteLock rwl = new ReentrantReadWriteLock();
    
        public static void main(String[] args) {
            // 1 读取缓存里面的数据
            // cache.query()
            // 2 如果换成没数据,则取数据库里面查询  database.query()
            // 3 查询完成之后,数据塞到塞到缓存里面 cache.put(data)
        }
    
        public Object get(String id) {  
            Object value = null;
            // 首先开启读锁,从缓存中去取
            rwl.readLock().lock();
            try {
                if (map.get(id) == null) {
                    // TODO database.query();  全部查询数据库 ,缓存雪崩
                    // 必须释放读锁
                    rwl.readLock().unlock();
                    // 如果缓存中没有释放读锁,上写锁。如果不加锁,所有请求全部去查询数据库,就崩溃了
                    rwl.writeLock().lock(); // 所有线程在此处等待  1000  1  999 (在同步代码里面再次检查是否缓存)
                    try {
                        // 双重检查,防止已经有线程改变了当前的值,从而出现重复处理的情况
                        if (map.get(id) == null) {
                            // TODO value = ...如果缓存没有,就去数据库里面读取
                        }
                        rwl.readLock().lock(); // 加读锁降级写锁,这样就不会有其他线程能够改这个值,保证了数据一致性
                    } finally {
                        rwl.writeLock().unlock(); // 释放写锁@
                    }
                }
            } finally {
                rwl.readLock().unlock();
            }
            return value;
        }
    }
    

    问题滞留,异常的时候,释放需要释放那些锁才方便

    Condition

    用于替代wait/notifyo

    Object中的wait(),notify(),notifyAll()方法是和synchronized配合使用的,可以唤醒一个或者全部(单个等待集);

    Condition是需要与Lock配合使用的,提供多个等待集合,更精确的控制(底层是park/unpark机制);

    示例6

    // condition 实现队列线程安全。
    public class QueueDemo {
        final Lock lock = new ReentrantLock();
        // 指定条件的等待 - 等待有空位
        final Condition notFull = lock.newCondition();
        // 指定条件的等待 - 等待不为空
        final Condition notEmpty = lock.newCondition();
    
        // 定义数组存储数据
        final Object[] items = new Object[100];
        int putptr, takeptr, count;
    
        // 写入数据的线程,写入进来
        public void put(Object x) throws InterruptedException {
            lock.lock();
            try {
                while (count == items.length) // 数据写满了
                {
                    notFull.await(); // 写入数据的线程,进入阻塞
                }
                items[putptr] = x;
                if (++putptr == items.length) {
                    putptr = 0;
                }
                ++count;
                notEmpty.signal(); // 唤醒指定的读取线程
            } finally {
                lock.unlock();
            }
        }
        // 读取数据的线程,调用take
        public Object take() throws InterruptedException {
            lock.lock();
            try {
                while (count == 0) {
                    notEmpty.await(); // 线程阻塞在这里,等待被唤醒
                }
                Object x = items[takeptr];
                if (++takeptr == items.length) {
                    takeptr = 0;
                }
                --count;
                notFull.signal(); // 通知写入数据的线程,告诉他们取走了数据,继续写入
                return x;
            } finally {
                lock.unlock();
            }
        }
    }
    
    记得加油学习哦^_^
  • 相关阅读:
    mysql grant命令
    appache ab测试高并发
    转:windows下定时执行备份数据库
    linux设置定时任务
    YII学习总结6(模板替换和“拼合”)
    YII学习总结5(视图)
    YII学习总结4(cookie操作)
    把字符串转换成整数
    不用加减乘除做加法
    求1+2+3+4+...+n
  • 原文地址:https://www.cnblogs.com/shaoyayu/p/14073941.html
Copyright © 2011-2022 走看看