这章很重要,相对其他同步组件难,但是也很重要。
package com.mmall.concurrency.AQS;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class countdownlatchDemo {
private final static int threadCount = 200;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
test(threadNum);
} catch (Exception e) {
log.error("exception", e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
log.info("finish");
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
Thread.sleep(100);
log.info("{}", threadNum);
Thread.sleep(100);
}
}
// 只等待 test2 方法30 毫秒
countDownLatch.await(30, TimeUnit.MILLISECONDS);
它用于仅能提供有限访问的资源,比如数据库连接输入最大20,做并发访问控制
代码如下,
import java.util.concurrent.*;
@Slf4j
public class SemaphoreDemo1 {
private final static int threadCount = 50;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
// final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 定义一个信号量,每次允许并发20个
final Semaphore semaphore = new Semaphore(20);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire();
test3(threadNum);
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test3(int threadNum) throws Exception {
Thread.sleep(100);
log.info("{}", threadNum);
}
}
但是有时候我们不一定一次拿到一个许可,而是多个。来看看多个的情况。
semaphore.acquire(5);
test3(threadNum);
semaphore.release(5);
但是还有一种情况,有时候并发实在太高了,我们想少点,多了之后就丢弃。重点看tryAcquire() 这个方法。代超时时间的参数。
if (semaphore.tryAcquire(1000,TimeUnit.MILLISECONDS)){
test3(threadNum);
semaphore.release();
}
获取一个许可
获取多个许可
尝试获取一个许可
尝试等待获取一个许可
尝试等待获取多个许可
countdownlatch 和 CylicBarrier相似:
package com.mmall.concurrency.AQS;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CycliBarrierDemo {
private static int threadNum;
// 等待的线程数够5个就执行
static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception {
//线程池
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i< 10; i++){
final int threadNum = i;
Thread.sleep(1000); //执行前睡一会
executorService.execute( () -> {
try{
race(threadNum);
}catch (Exception e){
log.error("race error :",e);
}
});
}
}
public static void race(int threadNum) throws Exception{
Thread.sleep(1000);
log.info("{} is ready.", threadNum);
cyclicBarrier.await();
log.info("{} is continue.", threadNum);
}
}
cyclicBarrier.await(1000, TimeUnit.MILLISECONDS); 会报错
19:06:29.114 [pool-1-thread-1] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 0 is ready.
19:06:30.112 [pool-1-thread-2] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 1 is ready.
19:06:30.189 [pool-1-thread-1] ERROR com.mmall.concurrency.AQS.CycliBarrierDemo - race error :
java.util.concurrent.TimeoutException: null
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
at com.mmall.concurrency.AQS.CycliBarrierDemo.race(CycliBarrierDemo.java:38)
// 改造之后
19:09:57.986 [pool-1-thread-1] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 0 is ready.
19:09:58.981 [pool-1-thread-2] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 1 is ready.
19:09:58.998 [pool-1-thread-1] WARN com.mmall.concurrency.AQS.CycliBarrierDemo - ===============出错?别出来
19:09:58.998 [pool-1-thread-2] WARN com.mmall.concurrency.AQS.CycliBarrierDemo - ===============出错?别出来
19:09:58.998 [pool-1-thread-1] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 0 is continue.
19:09:58.998 [pool-1-thread-2] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 1 is continue.
// 等待的线程数够5个就执行
static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,
() ->{log.info("====running"); });
运行结果:
19:13:26.624 [pool-1-thread-5] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 4 is ready.
19:13:26.624 [pool-1-thread-5] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - ====running
19:13:26.624 [pool-1-thread-5] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 4 is continue.
19:13:26.625 [pool-1-thread-1] INFO com.mmall.concurrency.AQS.CycliBarrierDemo - 0 is continue.
reentrantlock 阻止锁在用户级别处理,而不进入内核阻塞,用CAS原理
// 定义锁
private static Lock lock = new ReentrantLock();
==============================
private static void add(){
lock.lock();
try{
count++;
}catch (Exception e){
log.error("{} add 报错", e.getMessage());
}finally {
lock.unlock();
}
}
ReentrantReadWriteLock:在没有任何读写锁的时候才可以取得写入锁,读取锁定。
package com.mmall.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
public class ReadWriteLock {
private final Map map = new TreeMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
// 获取方法
public Data get(String key){
readLock.lock();
try{
return map.get(key);
}finally {
readLock.unlock();
}
}
// 获取一大堆方法
public Set getAllKeys(){
readLock.lock();
try{
return map.keySet();
}finally {
readLock.unlock();
}
}
// put func
public Data put(String key, Data value){
writeLock.lock();
try{
return map.put(key,value);
}finally {
readLock.unlock();
}
}
// Data 方法
class Data{
}
}
主要分析悲观乐观的区别
package com.mmall.concurrency.lock;
import java.util.concurrent.locks.StampedLock;
public class StampLockDemo {
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看乐观读锁案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
double currentX = x, currentY = y; //将两个字段读入本地局部变量
if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
try {
currentX = x; // 将两个字段读入本地局部变量
currentY = y; // 将两个字段读入本地局部变量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲观读锁案例
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
if (ws != 0L) { //这是确认转为写锁是否成功
stamp = ws; //如果成功 替换票据
x = newX; //进行状态改变
y = newY; //进行状态改变
break;
} else { //如果不能成功转换为写锁
sl.unlockRead(stamp); //我们显式释放读锁
stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
}
}
} finally {
sl.unlock(stamp); //释放读锁或写锁
}
}
}
}
// 定义锁
private static StampedLock stampedLock = new StampedLock();
=========
private static void add1(){
long stamp = stampedLock.writeLock();
try{
count++;
}catch (Exception e){
log.error("{} add 报错", e.getMessage());
}finally {
stampedLock.unlock(stamp);
}
}
多线程间协调通信工具类,条件具备,等待进程唤醒。
很少用,面试知道即可
package com.mmall.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class ConditionLock {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
new Thread(() -> {
try {
reentrantLock.lock();
log.info("wait signal"); // 1
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get signal"); // 4
reentrantLock.unlock();
}).start();
new Thread(() -> {
reentrantLock.lock();
log.info("get lock"); // 2
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signalAll();
log.info("send signal ~ "); // 3
reentrantLock.unlock();
}).start();
}
}