1、线程接力
案例:第一个线程打印5次,第二个线程打印10次,第三个线程打印15次,第三个线程执行完毕后,
再从第一个线程继续打印,执行第2轮的操作,总共执行10轮
线程接力代码
package com.thread.lock.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class TestCondition {
Lock lock = new ReentrantLock();
Condition con1 = lock.newCondition();
Condition con2 = lock.newCondition();
Condition con3 = lock.newCondition();
private int flg = 1;
public void product1(int j) {
lock.lock();
try {
while (flg != 1) {
try {
con1.await();
} catch (InterruptedException e) {
}
}
con2.signal();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i + "第" + j + "轮");
}
flg = 2;
} finally {
lock.unlock();
}
}
public void product2(int j) {
lock.lock();
try {
while (flg != 2) {
try {
con2.await();
} catch (InterruptedException e) {
}
}
con3.signal();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i + "第" + j + "轮");
}
flg = 3;
} finally {
lock.unlock();
}
}
public void product3(int j) {
lock.lock();
try {
while (flg != 3) {
try {
con3.await();
} catch (InterruptedException e) {
}
}
flg = 1;
con1.signal();
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i + "第" + j + "轮");
}
} finally {
lock.unlock();
}
}
}
public class LockConditionTest {
public static void main(String[] args) {
TestCondition ts = new TestCondition();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ts.product1(i + 1);
}
}, "第一个线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ts.product2(i + 1);
}
}, "第er个线程").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ts.product3(i + 1);
}
}, "第san个线程").start();
}
}
在上面的线程接力上用到了java.util.concurrent.locks.Lock、ReentrantLock、Condition
Lock和Synchronized之间的区别:
1、Synchronized是java关键字,给对象加锁,而且这个锁是我们不能控制的(jvm加锁),而lock是juc中提供的接口(他的实现ReentrantLock),由于是用户自己创建出来的锁,所以这个用户是可以控制的。
2、synchronized加锁后,必须获取监听器才可以执行,且他加锁后,是无法中断的,但是lock是可以中断的。
3、Synchronized释放锁是有顺序的,先加的锁后释放,而lock没有这个限制,可以随时的释放锁。
4、lock在使用时,是不可以在使用wait,notify,notifyAll方法,否则会报错的,他使用到了Condition的方法来代替了这几个方法,分别是await、signal、signalAll
2、案例:多个线程访问缓存,如果缓存中没有,读取数据库,如果有,直接获取。
可以参考一下思想来实现:
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}}
//ReentrantReadWriteLocks can be used to improve concurrency in some uses of some kinds of Collections. This is typically worthwhile only //when the collections are expected to be large, accessed by more reader threads than writer threads, and entail operations with overhead that //outweighs synchronization overhead. For example, here is a class using a TreeMap that is expected to be large and concurrently accessed.
class RWDictionary {
private final Map m = new TreeMap();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}}
这里使用ReadWriteLock读写锁来实现这个功能
这个锁也是由我们随意控制的。案例:100个线程读取,1个线程写入
class RedSpider1 {
private String data = "";
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void write(String data) {
lock.writeLock().lock();
try {
this.data = data;
System.out.println("教师端输入数据 = " + data);
} finally {
lock.writeLock().unlock();
}
}
public String read() {
lock.readLock().lock();
try {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学生端" + Thread.currentThread().getName() + "读取数据 = " + data);
return data;
} finally {
lock.readLock().unlock();
}
}
}
public class TestJUC_05_rw_lock {
public static void main(String[] args) throws Exception {
final RedSpider1 rs = new RedSpider1();
// 多个线程的读取不需要加锁。
// 一个线程写数据的时候不能被其他线程读取,应该增加锁。
// 多个线程同时写入数据,也应该加锁。
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
rs.read();
}
}, "" + i).start();
}
Thread.sleep(100);
new Thread(new Runnable() {
public void run() {
rs.write("java juc...");
}
}).start();
}
}
synchronized是关键字,他在加锁后是不能被打断的,因为他是系统加的锁机制(jvm),用户是控制不了。而ReetrantLock是Lock接口的实现类,这个类是可以在加锁后,是可以打断的,是受自己控制的,比较灵活,synchronized和ReentrantLock的区别
而ReetrantLock是不能和wait、notify、notifyAll这些搭配使用的,他一般的用Codition这额条件类的方法的await来替代Object类的wait,用signal来替代notify的,signalAll来替代notifyAll,Lock lock=new ReetrantLock();
Condition c=lock.newCondition();
这个ReetrantLock是一定要自己关闭的,而synchronized是不要我们自己去关闭的,
Synchronized释放锁的顺序是先加的后释放,而ReetrantLock释放锁比较灵活,可以自己定义