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();
}
}
}
记得加油学习哦^_^