赞
踩
并发编程领域,有两大核心问题:
这两大问题,管程都是能够解决的。
Java SDK 并发包通过 Lock 和 Condition 两个接口来实现管程,其中 Lock 用于解决互斥问题,Condition 用于解决同步问题。
1、能够响应中断。
synchronized 的问题是,持有锁 A 后,如果尝试获取锁 B 失败,那么线程就进入阻塞状态,一旦发生死锁,就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号,也就是说当我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。
2、支持超时。
如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。
3、非阻塞地获取锁。
如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。
支持中断的 API
void lockInterruptibly()
throws InterruptedException;支持超时的 API
boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;支持非阻塞获取锁的 API
boolean tryLock();
它是利用了 volatile 相关的 Happens-Before 规则。Java SDK 里面的 ReentrantLock,内部持有一个 volatile 的成员变量 state,获取锁的时候,会读写 state 的值;解锁的时候,也会读写 state 的值。
ReentrantLock 可重入锁,线程可以重复获取同一把锁。有两个构造函数,一个是无参构造函数,一个是传入 fair 参数的构造函数。fair 参数代表的是锁的公平策略,如果传入 true 就表示需要构造一个公平锁,反之则表示要构造一个非公平锁。
1、永远只在更新对象的成员变量时加锁
2、永远只在访问可变的成员变量时加锁
3、永远不在调用其他对象的方法时加锁
synchronized 管程里只有一个条件变量,而 Lock&Condition 实现的管程是支持多个条件变量的,在很多并发场景下,支持多个条件变量能够让我们的并发程序可读性更好,实现起来也更容易。
Lock 和 Condition 实现的管程,线程等待和通知需要调用 await()、signal()、signalAll();
synchronized 实现的管程才能使用 wait()、notify()、notifyAll() ;
Semaphore 可以允许多个线程访问一个临界区,当多个线程进入临界区时,如果需要访问共享变量就会存在并发问题,所以必须加锁,Semaphore 需要锁中锁。
信号量模型:一个计数器,一个等待队列,三个方法。在信号量模型里,计数器和等待队列对外是透明的,所以只能通过信号量模型提供的三个方法来访问它们,这三个方法分别是:init()、down() 和 up(),三个方法都是原子性的,并且这个原子性是由信号量模型的实现方保证的。
1、init():设置计数器的初始值。
2、down():计数器的值减 1;如果此时计数器的值小于或者等于 0,则当前线程将被阻塞,否则当前线程可以继续执行。
3、up():计数器的值加 1;如果此时计数器的值大于 0,则唤醒等待队列中的一个线程,并将其从等待队列中移除。
- 在 Java SDK 并发包里,down() 和 up() 对应的则是 acquire() 和 release()。
各种池化资源,例如连接池、对象池、线程池等等。
init() 初始化指定数量的凭证,当多个线程同时访问,每个线程调用 acquire() 方法获取一个凭证,当凭证数量归零,获取不到凭证的线程将阻塞在等待队列中,持有凭证的线程调用 release() 归还凭证,同时唤醒一个阻塞队列中的线程,并将其移除阻塞队列。acquire() 与 release()成对使用
读写锁类似于 ReentrantLock,也支持公平模式和非公平模式。读锁和写锁都实现了 java.util.concurrent.locks.Lock 接口,所以支持 lock() 方法、tryLock()
、lockInterruptibly() 等方法也都是支持的。但是有一点需要注意,那就是只有写锁支持条件变量,读锁是不支持条件变量的,读锁调用 newCondition() 会抛出
UnsupportedOperationException 异常。
所有的读写锁都遵守以下三条基本原则:
1、允许多个线程同时读共享变量;
2、只允许一个线程写共享变量;
3、如果一个写线程正在执行写操作,此时禁止读线程读共享变量。
class Cache<K, V> { final Map<K, V> m = new HashMap<>(); final ReadWriteLock rwl = new ReentrantReadWriteLock(); // 读锁 final Lock r = rwl.readLock(); // 写锁 final Lock w = rwl.writeLock(); V get(K key) { V v = null; // 读缓存 r.lock(); try { return m.get(key); } finally { r.unlock(); } // 缓存中存在,返回 if (v != null) { return v; } // 缓存中不存在,查询数据库 w.lock(); try { // 再次验证 // 其他线程可能已经查询过数据库 v = m.get(key); if
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。