03 JUC并发编程2
JUC并发编程2
8、常用的辅助类
8.1、CountDownLatch
允许一个或多个线程等待直到其他线程中执行的一组操作完成同步辅助
计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//总数是6,必须要执行任务的时候,再执行!
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i <= 6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"get out");
countDownLatch.countDown();// 计数器-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,然后再向下执行,需要抛出异常
System.out.println("close door");
}
}
原理:
countDownLatch.countDown();// 计数器-1
countDownLatch.await();//等待计数器归零,然后再向下执行
每次有线程调用countDownLatch.countDown();// 计数器-1
,调用这个方法不会阻塞,假设计数器变为0,countDownLatch.await();
就会被唤醒,继续执行
8.2、CyclicBarrier
允许一组线程全部等待彼此达到共同屏障点,同步辅助
加法计数器
public class CyclicBarrierDemo {
public static void main(String[] args) {
//集齐7颗龙珠召唤神龙
//召唤龙珠的线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功");
});
for (int i = 0; i <= 7; i++) {
//加final,变量会存在堆中的方法区里,
//子线程共享进程的堆,所以能读到。否则会存在另一个线程的栈中,不同线程读不到
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集了"+temp);
try {
//通过await来计数,await之后本次线程会被阻塞,
// 直到神龙召唤后,才执行后面的
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3、Semaphore
一个计数信号量,在概念上,信号量维持一组许可证。如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它
6个车 ----> 3个停车位
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量:停车位 限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i <= 6; i++) {
new Thread(()->{
//acquire() 得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//release() 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire();
得到,假如已经满了,等待,等待被释放为止
semaphore.release();
释放,会将当前的信号量 +1 然后唤醒等待的线程
作用:多个共享资源互斥的使用! 并发限流,控制最大的线程数
9、读写锁
public interface ReadWriteLock
ReadWriteLock 维护一对关联的locks,read lock可以由多个阅读器线程同时进行,只要没有作者,write lock是独家的(读可以被多线程同时读,写的时候只能有一个线程去写)
独占锁(写锁)一次只能被一个线程占用
共享锁(读锁)多个线程可以同时占用
读-读 可以共存!
读-写 不可以共存!
写-写 不可以共存!
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
for (int i = 1; i <=5 ; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.put(temp + "",temp + "");
},String.valueOf(i)).start();
}
for (int i = 1; i <=5 ; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.get(temp + "");
},String.valueOf(i)).start();
}
}
}
//自定义缓存区
class MyCacheLock{
//volatile 只能保证可见性,原理就是强制从主存去读取这个变量,而不是 //从方法栈中去读取,保证了线程之间的可见性,在并发操作时是可以被任何 //一个线程修改的,所以只有可见性没有原子性
private volatile Map<String,Object> map = new HashMap<String,Object>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存,写,写入的时候,只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName() + "写入ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
//取,读,所有人都可以读
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
10、阻塞队列
写入:如果队列满了,就必须阻塞等待
读取:如果队列是空的,必须阻塞等待生产
阻塞队列
BlockingQueue ---> Queue -----> Collection
什么情况下我们会使用阻塞队列:多线程并发处理,线程池
使用队列
添加、移除
四组API
**
方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer() |
移除 | remove() | poll() | take() | poll() |
检测队首元素 | element() | peek() | · | · |
1、抛出异常 |
2、不会抛出异常
3、阻塞 等待
4、超时等待
//抛出异常
public static void test1(){
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.add("a"));//true
System.out.println(blockingQueue.add("b"));//true
System.out.println(blockingQueue.add("c"));//true
//IllegalStateException: Queue full 抛出异常
// System.out.println(blockingQueue.add("d"));
//查看队首元素
System.out.println(blockingQueue.element());//a
System.out.println(blockingQueue.remove());//a
System.out.println(blockingQueue.remove());//b
System.out.println(blockingQueue.remove());//c
//NoSuchElementException: Queue full 抛出异常
System.out.println(blockingQueue.remove());
//有返回值,没有异常
public static void test2(){
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a"));//true
System.out.println(blockingQueue.offer("b"));//true
System.out.println(blockingQueue.offer("c"));//true
System.out.println(blockingQueue.offer("d"));//false,不能抛异常
//查看队首元素
System.out.println(blockingQueue.peek());//a
System.out.println(blockingQueue.poll());//a
System.out.println(blockingQueue.poll());//b
System.out.println(blockingQueue.poll());//c
System.out.println(blockingQueue.poll());//null
}
//等待,阻塞(一直阻塞)
public static void test3() throws InterruptedException {
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
//一直阻塞
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
blockingQueue.put("d");//队列没有位置了,一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//没有这个元素,一直阻塞
}
//等待,阻塞(等待超时)
public static void test4() throws InterruptedException {
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a"));//true
System.out.println(blockingQueue.offer("b"));//true
System.out.println(blockingQueue.offer("c"));//true
System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));//等待超过2秒就退出 false
System.out.println(blockingQueue.poll());//a
System.out.println(blockingQueue.poll());//b
System.out.println(blockingQueue.poll());//c
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));//等待超过2秒就退出 null
}
10.1 同步队列
SynchronousQueue
没有容量,大小
进去一个,必须等待,取出来之后,才能再往里面放一个元素
//同步队列
//和其他的BlockingQueue 不一样,SynchronousQueue 不存储元素
//put了一个元素,必须从里面take取出来,否则不能再put进去值
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + "put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + "put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + "put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + "-->" + blockingQueue.take());
System.out.println(Thread.currentThread().getName() + "-->" + blockingQueue.take());
System.out.println(Thread.currentThread().getName() + "-->" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)