Lock比传统线程模型中的synchronized方式更加面向对象, 与生活中的锁类似, 锁本身应该是一个对象. 两个线程执行的代码片段要实现同步互斥的效果, 谈么必须用一个Lock对象. 锁是在代表要操作的资源的类的内部方法中, 而不是线程代码中!
ReentrantLock(重入锁)
在需要进行同步的代码部分加上锁,但不要忘记最后一定要释放锁,不然会造成锁永远无法释放,其它线程永远进不来的结果使用try{ lock.lock() }finally{ lock.unlock() }
我们在使用synchronized的时候,如果需要多线程间进行写作工作, 则需要Object的wait()和notify(),notifyAll()方法进行配合工作
ReentrantReadWriteLock(读写锁)
之前学synchronized,ReentrantLock时,同一时间,只能有一个线程进行访问被锁定的代码, 那么读写锁则不同,其本质是分成两个锁,即读锁,写锁. 在读锁下,多个线程可以并发的进行访问, 但是在写锁的时候,只能一个一个的顺序访问
口诀: 读读共享, 写写互斥, 读写互斥
如果写多读少的话 那么还是使用重入锁 效率高
简单的缓存系统
/**
* 实现一个缓存系统
*
* @author user
*/
public class CacheDemo {
// 定义缓存Map
private Map<String, Object> cache = new HashMap<>();
// 定义读写锁
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public Object getData(String key) {
// 读锁开启 可以共享读
readWriteLock.readLock().lock();
Object value = null;
try {
value = cache.get(key);
if (null == value) {// 缓存中不存在该对象
// 释放读锁 开启写锁
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
// 重复检查 缓存是否有效的状态 因为在我们写数据之前
// 其他线程可能已经请求了写锁 并且使得缓存有效
if (null == value) {
value = "aaa";// 实际去queryDB()
// 放入缓存中
cache.put(key, value);
}
// 释放写锁 之前先获得写锁
readWriteLock.readLock().lock();
readWriteLock.writeLock().unlock();// 写锁释放, 持有读锁
}
} finally {
// 释放读锁
readWriteLock.readLock().unlock();
}
return value;
}
}
Condition
Condition的功能类似在传统技术中的Object.wait和Object.notify的功能, 在等待Condition时, 允许发生虚假唤醒, 这通常作为对基础平台语义的让步, 杜宇大多数应用程序, 这带来的实际影响很小, 因为Condition应该总是在一个循环中被等待, 并测试正在被等待的状态声明. 某个实现可以随意移除可能的虚假唤醒, 但是建议应用程序程序员总是假定这些虚假唤醒可能发生, 因此总是在一个循环中等待
一个锁内部可以有多个Condition, 即有多路等待和通知, 可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例, 从中除了要体味算法, 还要体味面向对象的封装, 在传统的线程机制中一个监视器对象只能有一路等待和通知, 要想实现多路等待和通知, 必须嵌套使用多个同步监视器对象(如果只用一个Condition, 两个)
那么同样,我们在使用lock的时候,可以使用一个新的等待/通知的类,他就是Condition,这个Condition一定是针对具体的某一把锁的,也就是在只有锁的基础上才会产生Condition
Condition await() Object wait()
多Condition
我们可以通过一个Lock对象产生多个Condition进行多线程的交互,非常的灵活,可以使得部分需要唤醒的线程唤醒,其它线程则继续等待通知
Condition signal() Object notify()
代码
package multi.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionCommunication {
public static void main(String[] args) {
final Bussiness bussiness = new Bussiness();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
bussiness.sub(i);
}
}
}).start();
for (int i = 1; i <= 50; i++) {
bussiness.main(i);
}
}
}
class Bussiness {
private boolean flag = true;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void sub(int j) {
lock.lock();
try {
// 如果flag为false则等待
while (!flag) {// 使用while 比较好 可以防止假唤醒
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 10; i++) {
System.out.println("sub : " + i + " 第" + j + "次外循环");
}
// 将flag置为false 并唤醒主线程
flag = false;
condition.signal();
} finally {
lock.unlock();
}
}
public void main(int j) {
lock.lock();
try {
// 如果flag为true则等待
while (flag) {
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 5; i++) {
System.out.println("main : " + i + " 第" + j + "次外循环");
}
// 将flag置为true 并唤醒子线程
flag = true;
condition.signal();
} finally {
lock.unlock();
}
}
}
公平锁/非公平锁 非公平锁效率高 默认非公平