JUC-2
JUC-2
6、集合类不安全
list
package demo02;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//.ConcurrentModificationException 并发修改异常
public class Day92300 {
public static void main(String[] args) {
/*
并发下ArrayList不安全的吗,Synchronized ;
解决方案;
1、List<String>List = new Vector<> ();
2、List<String>list = Collections.synchronizedList(new ArrayList<>());
3、List<String> list = new CopyOnwriteArrayList<>();
Copyonwrite 写入时复制Cow计算机程序设计领域的一种优化策略;
多个线程调用的时候,List,读取的时候,固定的,写入(覆盖)
在写入的时候避免覆盖,造成数据问题!
读写分离Mycat
*/
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
//UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,
// 主要是让让分布式系统中的所有元素,都能有唯一的辨识信息。随机生成UUID的标识符是UUID类中的方法
// 而UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
set
package demo02;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
//ConcurrentModificationException,注:线程少可能不会出异常
public class Day92301 {
public static void main(String[] args) {
//2种方法
// set<string> set = new Hashset<>();
//set<string> set = collections.synchronizedset(new HashSet<>());
Set<String> set = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
//UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,
// 主要是让让分布式系统中的所有元素,都能有唯一的辨识信息。随机生成UUID的标识符是UUID类中的方法
// 而UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
hashSet 底层是什么?
public Hashset( {
map = new HashMap<>);
// add set本质就是map key是无法重复的!
public boolean add(E e) {
return map. put(e,PRESENT)==nu1l;
private static final object PRESENT = new object(;// 不变的值!
Map不安全
package demo02;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
//ConcurrentModificationException
public class Day92302 {
public static void main(String[] args) {
/*
map是这样用的吗?不是,工作中不用HashMap
默认等价于什么?new HashMap<>(16,0.75);
Map<String, String> map = new HashMap<>();
唯一的一个家庭作业:研究concurrentHashMap的原理
*/
Map<String,String> map=new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
7、Callable
1、可以有返回值
2、可以抛出异常
3、方法不同,run()/ call()
new Thread( new FutureTask<V>( caLlable ) ).start();
package demo03;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Day92500 {
public static void main(String[] args) {
Mythread mythread = new Mythread();
FutureTask futureTask = new FutureTask(mythread);
Thread thread = new Thread(futureTask);//结果会被缓存,效率高
thread.start();
Object o = null;
try {
o = futureTask.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
System.out.println(o);
}
}
class Mythread implements Callable{
@Override
public Object call() throws Exception {
System.out.println("999");
return 555;
}
}
Integer o = (Integer) futureTask.get();//这个get方法可能
会产生阻塞!把他放到最后
或者使用异步通信来处理!
细节:
1、有缓存
2、结果可能需要等待,会阻塞!
8、常用的辅助类
8.1、CountDownLatch
package demo03;
import java.util.concurrent.CountDownLatch;
//计数器
public class Day92501 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(6);//总数为6
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" go out");
countDownLatch.countDown();//数量减1
},String.valueOf(i)).start();
}
try {
countDownLatch.await();//等待计数器归零,然后再向下执行
//必须要执行任务的时候,再使用!
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("close");
}
}
原理:
- countDownLatch. countDown;/数量-1
- countDownLatch.awaitO;//等待计数器归零,然后再向下执行
- 每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行!
8.2、CyclicBarrier
加法计数器
package demo03;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Day92502 {
public static void main(String[] args) {
//集齐7颗龙珠召唤神龙
//召唤龙珠的线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("7777");
});
for (int i = 0; i < 7; i++) {
final int temp=i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集第"+temp+"颗龙珠");
try {
cyclicBarrier.await();//等待
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
8.3、Semaphore
信号量
package demo03;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class Day92503 {
public static void main(String[] args) {
//线程数量:停车位,限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire();//得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
semaphore.release();//释放
}
}).start();
}
}
}
原理:
- semaphore.acquire()
获得,假设如果已经满了,等待,等待被释放为止! - semaphore.release();
释放,会将当前的信号量释放+ 1,然后唤醒等待的线程! - 作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!
9、读写锁
ReadWriteLock
package demo03;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Day92504 {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
//写入
for (int i = 0; i < 5; i++) {
int temp=i;
new Thread(()->{
myCache.put(temp+"",temp+"");
}).start();
}
//读取
for (int i = 0; i < 5; i++) {
int temp=i;
new Thread(()->{
myCache.get(temp+"");
}).start();
}
}
}
//自定义缓存
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
//存
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成");
}
//取
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
}
}
//加锁的
class MyCacheLock{
private volatile Map<String,Object> map=new HashMap<>();
//读写锁:更加细粒度的控制
ReentrantReadWriteLock 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()+"写入完成");
} catch (Exception e) {
throw new RuntimeException(e);
} 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()+"读取完成");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
readWriteLock.readLock().unlock();
}
}
}
独占锁(写锁)一次只能被一个线程占有
共享锁(读锁)多个线程可以同时占有
ReadwriteLock
读-读可以共存!
读-写不能共存!
写-写不能共存!
10、阻塞队列
- 阻塞
- 队列
写入:如果队列满了,就必须阻塞等待取:
如果是队列是空的,必须阻塞等待生产
BlockingQueue BlockingQueue不是新的东西
什么情况下我们会使用阻塞队列:多线程并发处理,线程池!
什么情况下我们会使用阻塞队列:多线程并发处理,线程池!
学会使用队列
添加、移除
四组API
方式 | 抛出异常 | 有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer |
移除 | remove | poll | take | poll |
检测队列首 | element | peek |
//抛出异常
ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("a"));//true
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
//System.out.println(queue.add("d"));IllegalStateException
System.out.println("======");
System.out.println(queue.remove());//a
System.out.println(queue.remove());
System.out.println(queue.remove());
//System.out.println(queue.remove());NoSuchElementException
}
public void test(){
//有返回值,没有异常
ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);//队列大小
System.out.println(queue.offer("a"));
System.out.println(queue.offer("r"));
System.out.println(queue.offer("e"));//true
System.out.println(queue.offer("w"));//false
System.out.println("-------");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());//e
System.out.println(queue.poll());//null
}
等待,阻塞(一直阻塞)
public void test2() throws InterruptedException {
ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
//一直阻塞
queue.put("a");
queue.put("c");
queue.put("x");
// queue.put("x"); 队列没有位置了,一直阻塞
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
//System.out.println(queue.take());没有这个元素,一直阻塞
}
超时等待
public void test3() throws InterruptedException {
ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
queue.offer("a");
queue.offer("v");
queue.offer("c");
queue.offer("c",2, TimeUnit.SECONDS);//等待超过2秒就退出
System.out.println("=========");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll(2, TimeUnit.SECONDS));//等待超过2秒就退出. null
SynchronousQueue同步队列
没有容量
进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
put、take
//同步队列
//和其他的BLockingQueue不一样, SynchronousQueue不存储元素
// put 了一个元素,必须从里面先take取出来,否则不能在put进去值!
@Test
public void test4(){
SynchronousQueue<String> queue = new SynchronousQueue<String>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"put1");
queue.put("1");
System.out.println(Thread.currentThread().getName()+"put2");
queue.put("2");
System.out.println(Thread.currentThread().getName()+"put3");
queue.put("3");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"--"+queue.take());
System.out.println(Thread.currentThread().getName()+"--"+queue.take());
System.out.println(Thread.currentThread().getName()+"--"+queue.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
11、线程池(重点)
线程池:三大方法、7大参数、4种拒绝策略
池化技术
- 程序的运行,本质︰占用系统的资源!优化资源的使用!=>池化技术
- 线程池、连接池、内存池、对象池.....创建、销毁。十分浪费资源
- 池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
线程池的好处:
1、降低资源的消耗
2、提高响应的速度
3、方便管理。
线程复用、可以控制最大并发数、管理线程
package demo04;
import org.junit.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Day92600 {
//Executors工具类、3大方法
//使用了线程池之后,使用线程池来创建线程
@Test
public void test1(){
// ExecutorService threadPool = Executors.newSingleThreadExecutor();///单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
ExecutorService threadPool = Executors.newCachedThreadPool();// 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
///线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
//本质ThreadPoo1Executor ()
public ThreadPoo1Executor(int corePoolsize,//核心线程池大小
int maximumPoo1size,//最大核心线程池大小
long keepAliveTime,// 超时了没有人调用就会释放
Timeunit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程的,一般不用动
RejectedExecutionHand1er handle//拒绝策略
四种拒绝策略
@Test
public void test2() {
///自定义线程池!工作ThreadPooLExecutor
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy()银行满了,还有人进来,不处理这个人的,抛出异常
//new ThreadPoolExecutor.CallerRunsPolicy()///哪来的去哪里! main ok
//new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!|
);
try {
//最大承载: Deque +max
for (int i = 0; i < 19; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
///线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
最大线程到底该如何定义
1、CPU密集型,几核,就是几,可以保持cPu的效率最高!
2、Io密集型 >判断你程序中十分耗工o的线程,
程序15个大型任务 io十分占用资源!
获取CPU的核数
system.out.println(Runtime.getRuntime( ).availableProcessors( ));
12、四大函数式接口(必需掌握)
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口:只有一个方法的接口
-
超级多FunctionalInterface
-
简化编程模型,在新版本的框架底层大量应用!
-
foreach(消费者类的函数式接口)
-
Function函数式接口
Function函数型接口,有一个输入参数,有一个输出只要是函数型接口可以用lambda表达式简化
- 断定型接口
有一个输入参数,返回值只能是布尔值!
-
Consumer消费型接口
消费型接口:只有输入,没有返回值
-
Supplier供给型接口
供给型接口没有参数,只有返回值
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!