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();
        }
    }
}
记得加油学习哦^_^
原文地址:https://www.cnblogs.com/shaoyayu/p/14073941.html