在这里插入代码片
## 一、定义
线程与线程之间不是相互独立的个体,它们彼此之间需要相互通信和协作;
2.1、CountDownLatch
定义:主线程等待其他线程,其他线程结束后,主线程才能执行(这里的主线程不一定是就是main主线程,强调的是你们先执行,执行完我再执行),强调的是一个线程等待多个线程先执行完,然后才执行;
2.2、CyclicBarrier( 循环屏障)
定义: 等待的线程达到一定数目后再让它们继续执行(强调的是大家一起等待,人齐了才能执行),用户可以自定义 barrierAction参数, 则在阻塞线程数达到设定值屏障打开前,会调用barrierAction的run()方法完成用户自定义的操作;
2.3、Semaphore( 信号量)
定义:控制多个线程访问特定的资源,主要用于限流(限制线程数);
2.4、Wait/Notify
定义:基于Object类的wait和notify方法,进行线程间的通信;
1、CountDownLatch,CyclicBarrier,lock
package com.sun.springbootdemo.thread;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import java.text.DecimalFormat;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 10个人抢红包 —— 资源类
* 1、红包必须抢完;
* 2、10个人的红包累计金额不能超过总数,也就是10元
* 3、保证同时开始抢
*
* @author Sundz
* @date 2020/12/25 19:19
*/
@Log4j2
public class QiangHongBao {
public QiangHongBao(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
/**
* @field 红包总金额
*/
private volatile double total = 10;
private AtomicInteger numberOfPeople = new AtomicInteger(0);
/**
* @field 保证主线程不会先执行,必须等10个人抢完所有的红包后才能执行
*/
private CountDownLatch countDownLatch;
/**
* @field 数据格式化
*/
private final DecimalFormat decimalFormat = new DecimalFormat("#.00");
/**
* @field 保证10个人同时开始抢,也就是准备阶段,必须同步
*/
private final CyclicBarrier barrier = new CyclicBarrier(10, () -> System.out.println("所有人都准备好了,可以开始抢红包了 -->> Beginning!!!"));
private final Lock lock = new ReentrantLock();
public double handle() throws Exception {
// 等待 必须等10个人都准备好了 才能开始抢红包
barrier.await();
lock.lock();
// 如果红包金额为0了 则说明红包已经被抢完了
double random = 0;
// 保留2位小数即可
total = Double.parseDouble(decimalFormat.format(total));
long count = countDownLatch.getCount();
try {
if (total == 0) {
countDownLatch.countDown();
log.info(Thread.currentThread().getName() + " -->> 红包被抢完了,你手速太慢了!");
log.info("当前没抢红包的人数有:{}", count);
return 0;
}
// 防止红包没有被抢完 强制最后一个人获取红包的剩余金额
if (numberOfPeople.get() == 9) {
log.info("最后一个小伙伴( " + Thread.currentThread().getName() + ") -->> 抢到的红包金额为:{}元,红包剩余:{}元", total, 0);
// 此时红包已被全部抢完了
total = 0;
countDownLatch.countDown();
log.info("当前没抢红包的人数有:{}", count);
return total;
}
random = getRandom();
/**
* @field 改变红包金额
*/
total -= random;
// 统计抢红包的人数 +1
numberOfPeople.incrementAndGet();
log.info(Thread.currentThread().getName() + " -->> 抢到的红包金额为:{}元,红包剩余:{}元", random, total);
/**
* @field 每一个人抢完红后需要统计 防止主线程先执行 导致程序结束了
*/
countDownLatch.countDown();
log.info("当前没抢红包的人数有:{}", count);
} catch (Exception e) {
log.error(Thread.currentThread().getName() + "获取锁时出现了异常!");
} finally {
// 一定要释放锁 防止发生死锁
lock.unlock();
}
return random;
}
/**
* @field 获取红包随机数
*/
public double getRandom() {
Random random = new Random(System.nanoTime());
return Double.parseDouble(decimalFormat.format(random.nextDouble() * 2));
}
@Test
public void tets() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(10);
QiangHongBao q = new QiangHongBao(countDownLatch);
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
try {
q.handle();
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
long count = countDownLatch.getCount();
countDownLatch.await();
log.info("红包抢完啦,感谢各位的参与!");
}
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(10);
QiangHongBao q = new QiangHongBao(countDownLatch);
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
try {
q.handle();
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
long count = countDownLatch.getCount();
countDownLatch.await();
log.info("红包抢完啦,感谢各位的参与!");
}
}
2、Sychronized,Wait,NotifyAll
package com.sun.springbootdemo.thread;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
/**
* 多个线程交替执行 == >> 线程间需要通信
*
* @author Sundz
* @date 2020/12/24 19:00
*/
@Log4j2
public class AlternateExecute {
@Test
public void threadTest() {
CountDownLatch countDownLatch = new CountDownLatch(5);
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
Semaphore semaphore = new Semaphore(2);
Reset reset = new Reset();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
reset.increase();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
reset.increase();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
reset.decrease();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
reset.decrease();
}
}, "D").start();
//TimeUnit.SECONDS.sleep(5);
}
class Reset {
/**
* @field 复位状态
*/
int status = 0;
public synchronized void increase() {
try {
// 阻塞 等待 防止多次累加
while (status != 0) {
this.wait(); // 让出CPU的控制权 要用while防止虚假等待
}
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
status++;
log.info(Thread.currentThread().getName() + ", status:" + status);
// 需要通知其他线程 可以进行减操作了
this.notifyAll();
}
public synchronized void decrease() {
try {
// 当status为0时 需要等待
while (status == 0) {
this.wait(); // 让出CPU的控制权 要用while防止虚假等待
}
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
status--;
log.info(Thread.currentThread().getName() + ", status:" + status);
// 需要通知其他的线程 可以进行加操作了
this.notifyAll();
}
}
}
3、Lock,Condition
package com.sun.springbootdemo.thread;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A,B,C -->> 3个字母轮流执行
*
* @author Sundz
* @date 2020/12/28 20:07
*/
@Log4j2
public class RoundRobinExecution1 {
/**
* @field 输出字母数组
*/
private static final String[] alphabetArr = {
"A", "B", "C"};
private static final CountDownLatch latch = new CountDownLatch(9);
/**
* @field 多线程轮流输出 A, B, C, D
*/
static class Letter {
/**
* @field 可重入锁
*/
private final Lock lock = new ReentrantLock();
/**
* @field A监控器 对应输出A的线程
*/
private final Condition conditionA = lock.newCondition();
/**
* @field B监控器 对应输出B的线程
*/
private final Condition conditionB = lock.newCondition();
/**
* @field C监控器 对应输出C的线程
*/
private final Condition conditionC = lock.newCondition();
/**
* @field 用于标记线程
*/
private int count = 1;
/**
* 输出字母A
*
* @param letter 要输出的字母
* @return {@link String} 返回值
*/
public void printA(String letter) {
lock.lock();
try {
// 只要不是字母A则等待
while (count != 1) {
conditionA.await(); // 等待 交出CPU控制权
}
// 字母A 则输出,同时需要唤醒线程B(也就是输出B的线程)
log.info(Thread.currentThread().getName() + "-->输出的字母为:{}", letter);
count = 2; // 重置状态
conditionB.signal(); // 唤醒B线程
// A线程已完成
latch.countDown();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
lock.unlock(); // 必须在finally中释放锁 否则会引起死锁
}
}
/**
* 输出字母B
*
* @param letter 要输出的字母
* @return {@link String} 返回值
*/
public void printB(String letter) {
lock.lock();
try {
// 只要不是字母B则等待
while (count != 2) {
conditionB.await(); // 等待 交出CPU控制权
}
// 字母B 则输出,同时需要唤醒线程C(也就是输出C的线程)
log.info(Thread.currentThread().getName() + "-->输出的字母为:{}", letter);
count = 3; // 重置状态
conditionC.signal(); // 唤醒C线程
// B线程已完成
latch.countDown();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
lock.unlock(); // 必须在finally中释放锁 否则会引起死锁
}
}
/**
* 输出字母B
*
* @param letter 要输出的字母
* @return {@link String} 返回值
*/
public void printC(String letter) {
lock.lock();
try {
// 只要不是字母C则等待
while (count != 3) {
conditionC.await(); // 等待 交出CPU控制权
}
// 字母C 则输出,同时需要唤醒线程D(也就是输出D的线程)
log.info(Thread.currentThread().getName() + "-->输出的字母为:{}", letter);
count = 1; // 重置状态
conditionA.signal(); // 唤醒C线程
// C线程已完成
latch.countDown();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
lock.unlock(); // 必须在finally中释放锁 否则会引起死锁
}
}
@Test
public void threadTest() throws Exception {
Letter letter = new Letter();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
letter.printA(alphabetArr[0]);
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
letter.printB(alphabetArr[1]);
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
letter.printC(alphabetArr[2]);
}
}, "C").start();
//防止先主线程执行完
latch.await();
//TimeUnit.SECONDS.sleep(3);
}
}
}
赠人玫瑰,手有余香,请留下你的赞或者宝贵意见吧!