最新要闻
- 大冶法院执行干警客串“会计”解纠纷
- 好价速囤:杰士邦爆款超薄30只19.9元(240元大额券)_环球头条
- 国产首款带状疱疹疫苗正式上市:40岁就能打
- 世界新消息丨阅读馆加盟排行榜_春雨书店加盟
- 炎炎夏日必备!迷你可爱多冰淇淋官旗发车:每支不到1块钱 当前热文
- 环球今头条!马斯克刚走 黄仁勋要来!科技大佬排队来华:不能失去中国市场
- word插入excel表格为什么无法编辑(word插入EXCEL表格)
- 每年全球收专利费12亿美元!华为连续5年中国申请专利领先 5G必要专利全球第一|环球观察
- 8岁男孩摆摊1小时赚584元 直言生意太好:网友羡慕上一天班也没人赚的多
- 无开孔真全面屏iPhone要来了!苹果新专利可设计屏下Face ID-当前速看
- 新疆印象建设规划设计研究院_关于新疆印象建设规划设计研究院简介-天天快资讯
- 赛车T恤贵上天
- 【报资讯】国产新x86处理器登场 被指贴牌英特尔:回应站在巨人肩膀上 更上一层楼
- 每日快讯!北京西城区举办建筑工地防汛抢险应急救援演练
- 印度列车相撞事故已致120死超800伤 车头被撞扁:现场惨烈
- 今日热文:椰树集团首次回应直播风格争议:审美回归、主打真实自然
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
【播资讯】多线程安全的案例展示与解决方案
一、概念
1. 什么是线程安全
当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。
(资料图片)
通俗来说就是:不管业务中遇到怎么的多个线程访问某个对象或某个方法的情况,而在编写这个业务逻辑的时候,都不需要额外做任何额外的处理(也就是可以像单线程编程一样),程序也可以正常运行(不会因为多线程而出错),就可以称为线程安全。
2. 什么是线程不安全:
多个线程同时做一个操作时,如使用 set 设置一个对象的值时,如果同时有另一个线程使用 get 方法取该对象的值,就有可能取到不正确的值,这种情况就需要我们进行额外的操作保证结果正确,如Synchronized关键词修饰做同步
3. 那为什么不全部设计为线程安全:
主要考虑到运行速度、设计成本等因素。
二、出现线程安全的案例
什么情况下会出现线程安全问题,怎么避免?
- 运行
结果错误
:a++多线程下出现消失的请求现象。 - 线程的
活跃性
问题:死锁、活锁、饥饿 - 对象
发布和初始化
的时候的安全问题
1. a++ 在多线程下出现的数据运算出错
代码演示:
public class MultiThreadsError implements Runnable { static MultiThreadsError multiThreadsError = new MultiThreadsError(); int index = 0; public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(multiThreadsError); Thread thread2 = new Thread(multiThreadsError); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(multiThreadsError.index); } @Override public void run() { for (int i = 0; i < 10000; i++) { index++; } }}
结果如下:
两个线换各执行 10000 次 +1,结果却不是20000,运行过程中出现了什么问题呢?分析如下:
如上图,对于i++
这一步,两个线程之间会获取 i
的值然后去执行 ++
,比如线程1 拿到i
时i=1
,在执行 i+1
时,线程2又拿到了i
,这时依然是i=1
(因为线程1还没有运算结束),线程2也进行i+1
,这就导致两个线程都计算的结果都是2,然后都给i
赋值,最终i=2
,但其实是i+1
执行了两次,结果应该是i=3
,运算结果不符合预期;结果也就导致上面代码中的打印结果。
那么 a++ 具体在哪里冲突了?又冲突了几次??我们可以尝试作如下改进,打印出出错的地方
public class MultiThreadsError implements Runnable { static MultiThreadsError multiThreadsError = new MultiThreadsError(); // 通过一个boolean数组来标记已经++了的下标为true值,若如果已经为true,那么打印下标为越界线程错误的话; final boolean[] marked = new boolean[100000]; int index = 0; // AtomicInteger为细化步骤,就会让这次操作不会出现线程不安全操作,这里用他记录错误/正确次数 static AtomicInteger realInt = new AtomicInteger();//正确次数 static AtomicInteger errorInt = new AtomicInteger();//错误次数 //有参构造参数为2,代表需要等待两个线程经过,再放行 static volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2); static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(multiThreadsError); Thread thread2 = new Thread(multiThreadsError); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(multiThreadsError.index); System.out.println("正确次数为:"+realInt); System.out.println("错误次数为:"+errorInt); } @Override public void run() { marked[0] = true; for (int i = 0; i < 10000; i++) { //设置栅栏 try { cyclicBarrier2.reset(); cyclicBarrier1.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } index++; try { cyclicBarrier1.reset(); cyclicBarrier2.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } // 原子整数,是线程安全的,用来计数 ++ 执行的次数 realInt.incrementAndGet(); synchronized (multiThreadsError){ // 如果已经为true,说明此时的index所在的位置已经执行过一次 ++ 了,那么打印下标为越界线程错误的话; // if (marked[index]){ if (marked[index] && marked[index-1]){ // 原子整数,是线程安全的,用来失败的次数 errorInt.incrementAndGet(); System.out.println("该下标【"+index+"】越界被标记过了"); } marked[index] = true; } } }}
(1)定义 boolean[] marked 数组和 marked[index] = true 的作用?
通过一个boolean数组来标记已经++了的下标为true值,若如果已经为true,那么说明此时的 index 的当前值已经执行过一次 ++ 了,那就说明遇到了 index++ 冲突;
(1) 这里使用到了 CyclicBarrier 类,原因如下:
假设i=4
时,线程1和2遇到冲突,计算之后都给i
赋值了5,本该是一次错误运算,但是如果遇到下面的情景会将其认为是正确运算:此时的i=5
,线程1先拿到锁后设置 marked[5]=true
,然后线程1释放锁,此时线程1执行特别快,在线程2拿到锁时,线程1已经又执行了一次index++
,此时的 index=6
,线程2运行 if (marked[index])
时,即 marked[6]
明显还没设置,就不会认为是已经失败了。所以为了避免这种场景,保证线程1和2在交换锁期间,两个线程都只有一次 index++ 运算,就用到了 CyclicBarrier 类。
(2) 上面代码中为什么使用 if (marked[index] && marked[index-1])
,而不是使用if (marked[index])
,来作为失败运算的标记呢?
因为如果两个线程的 index++ 如果没有冲突的话,上个循环中的 index,和本次循环中的 index 应该是相差2,也就表示中间会少设置一个 marked[index],但是如果 marked[index-1] 已经被设置了,那就说明本次循环,两个线程的 index++ 冲突了,但是有一个特殊 marked[0],该值无论成功与否,都不会设置,所以需要在 run() 方法开头加上 marked[0] = true;
打印结果如下:
可以看到错误的次数和 表面上结果相加刚好是20000,同时也打印了发生错误的位置是19143,从而更清晰地知道哪里发生了 index++ 冲突。
2. 线程的活跃性
问题:死锁、活锁、饥饿
这里以死锁为例,代码展示如下:
public class MultiThreadError implements Runnable { int flag = 1; static Object o1 = new Object(); static Object o2 = new Object(); public static void main(String[] args) { MultiThreadError r1 = new MultiThreadError(); MultiThreadError r2 = new MultiThreadError(); r1.flag=1; r2.flag=0; Thread thread1 = new Thread(r1); Thread thread2 = new Thread(r2); thread1.start(); thread2.start(); } @Override public void run() { System.out.println("flag: "+flag); if (flag==1){ synchronized (o1){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ System.out.println("1"); } } } if (flag==0){ synchronized (o2){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("1"); } } } }}
结果如下:
在打印出上面两行之后,便不会再进行打印,而且程序也不会终止,这就是死锁。两个线程首先都各自持有一个锁,然后去抢夺另一把锁,但是要抢夺的锁都已经心有所属,分别就是所属于对方,于是两个线程就一直干耗着,进退两难,成了死局。
3. 对象发布和初始化的时候的安全问题
什么是对象发布:
让这个对象在超过这个类的范围去使用。比如先使用 public 声明这个类,那么这个类就是被发布出去了,那怎么超过这个类的范围去使用呢,如下:
- 如果一个方法内 return 返回了一个对象的话,任何调用这个方法的类,都会获取到这个对象
- 将某类的对象作为参数传递到其他类中,也是该类的对象脱离了本类,进入其他对象中
什么是逸出:
某个被发布到不该发布的地方,比如:
- 方法返回一个private对象(private对象本身是不让外部访问)
- 还未完成初始化(构造函数没完全执行完毕)就把对象提供给外界,比如以下几种情况:
- 在构造函数中未初始化完毕就给外部对象赋值this实例
- 隐式逸出——注册监听事件
- 在构造函数中运行子线程
3.1 方法返回一个private对象
(1) 代码展示:
public class MultiThreadError3 { private Map states; public MultiThreadError3(){ states=new HashMap<>(); states.put("1","周一"); states.put("2","周二"); states.put("3","周三"); states.put("4","周四"); states.put("5","周五"); states.put("6","周六"); states.put("7","周七"); } //这里逸出了 public Map getStates(){ return states; } //导致下面可以获取修改states对象的内容 public static void main(String[] args) { MultiThreadError3 multiThreadError3 = new MultiThreadError3(); Map states = multiThreadError3.getStates(); System.out.println(states.get("1")); states.remove("1"); System.out.println(states.get("1")); }}
打印结果如下:
states
这个Map 对象 本来是 MultiThreadError3
类私有的,但是在 getStates()
方法中被 return 出去了,那么外部就能拿到这个states ,而且甚至能对它进行操作,修改里面的值,这就可能造成很严重的安全问题。
解决方案:
通过返回副本的方式,避免直接让这个对象暴露给外界。
/** * 描述: 返回副本,解决逸出 */public class MultiThreadsError3 { private Map states; public MultiThreadsError3() { states = new HashMap<>(); states.put("1", "周一"); states.put("2", "周二"); states.put("3", "周三"); states.put("4", "周四"); } public Map getStates() { return states; } public Map getStatesImproved() { return new HashMap<>(states); } public static void main(String[] args) { MultiThreadsError3 multiThreadsError3 = new MultiThreadsError3(); Map states = multiThreadsError3.getStates();// System.out.println(states.get("1"));// states.remove("1");// System.out.println(states.get("1")); System.out.println(multiThreadsError3.getStatesImproved().get("1")); multiThreadsError3.getStatesImproved().remove("1"); System.out.println(multiThreadsError3.getStatesImproved().get("1")); }}
打印结果:
3.2 还未完成初始化(构造函数没完全执行完毕)就把this对象提供给外界
(1)代码演示:在构造函数中未初始化完毕就给外界对象赋值
public class MultiThreadsError4 { static Point point; public static void main(String[] args) throws InterruptedException { new PointMaker().start(); Thread.sleep(10); if (point != null) { System.out.println(point); } Thread.sleep(105); if (point != null) { System.out.println(point); } }}class Point { private final int x, y; public Point(int x, int y) throws InterruptedException { this.x = x; MultiThreadsError4.point = this; Thread.sleep(100); this.y = y; } @Override public String toString() { return x + "," + y; }}class PointMaker extends Thread { @Override public void run() { try { new Point(1, 1); } catch (InterruptedException e) { e.printStackTrace(); } }}
打印结果:
因为x的初始化比 y 要早一点,并且在构造函数中有线程睡眠,就可能导致在 main 函数中不同的时间输出的结果不一样,比如上图在main 函数中 Thread.sleep(10) 之后打印出的结果,和 Thread.sleep(105)之后打印出的结果不一样
(2) 代码演示:隐式逸出——注册监听事件
/** * 观察者模式 */public class MultiThreadsError5 { private int count; public MultiThreadsError5(MySource source) { source.registerListener(new EventListener() { @Override public void onEvent(Event e) { System.out.println("\n我得到的数字是" + count); } }); //模拟业务操作 for (int i = 0; i < 10000; i++) { System.out.print(i); } count = 100; } public static void main(String[] args) { MySource mySource = new MySource(); new Thread(() -> { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } mySource.eventCome(new Event() { }); }).start(); new MultiThreadsError5(mySource); } static class MySource { private EventListener listener; void registerListener(EventListener eventListener) { this.listener = eventListener; } void eventCome(Event e) { if (listener != null) { listener.onEvent(e); } else { System.out.println("还未初始化完毕"); } } } interface EventListener { void onEvent(Event e); } interface Event { }}
结果如下:
结果为什么是0而不是100呢?
在 new EventListener()
这个匿名内部类中,引用了外部类的 count
变量,这个匿名内部类就可以对它进行操作,如果 count 的值在构造函数中还没有初始化完成,就对该 count 进行操作,就导致count的值不准确。、
解决方案:
使用工厂模式,将构造器私有化不对外暴露,对外暴露一个方法:等做完所需的操作之后再 return 发布出去,就不会有实例过早被暴露的问题了。
/** * 描述: 用工厂模式修复刚才的初始化问题 */public class MultiThreadsError7 { int count; private EventListener listener; private MultiThreadsError7(MySource source) { listener = new EventListener() { @Override public void onEvent(MultiThreadsError5.Event e) { System.out.println("\n我得到的数字是" + count); } }; for (int i = 0; i < 10000; i++) { System.out.print(i); } count = 100; } public static MultiThreadsError7 getInstance(MySource source) { MultiThreadsError7 safeListener = new MultiThreadsError7(source); source.registerListener(safeListener.listener); return safeListener; } public static void main(String[] args) { MySource mySource = new MySource(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } mySource.eventCome(new MultiThreadsError5.Event() { }); } }).start(); MultiThreadsError7 multiThreadsError7 = new MultiThreadsError7(mySource); } static class MySource { private EventListener listener; void registerListener(EventListener eventListener) { this.listener = eventListener; } void eventCome(MultiThreadsError5.Event e) { if (listener != null) { listener.onEvent(e); } else { System.out.println("还未初始化完毕"); } } } interface EventListener { void onEvent(MultiThreadsError5.Event e); } interface Event { }}
打印结果:
(3) 在构造函数中新建线程
/** * 构造函数中新建线程 */public class MultiThreadError6 { private Map states; public MultiThreadError6(){ new Thread(new Runnable() { @Override public void run() { states=new HashMap<>(); states.put("1","周一"); states.put("2","周二"); states.put("3","周三"); states.put("4","周四"); states.put("5","周五"); states.put("6","周六"); states.put("7","周七"); } }).start(); } public Map getStates(){ return states; } public static void main(String[] args) { MultiThreadError6 multiThreadError6 = new MultiThreadError6(); System.out.println(multiThreadError6.states.get("1")); }}
打印结果:
上图所示:出现了空指针的情况 ,因为初始化的操作在另外一个线程中,可能那个线程没有执行完毕,就会出现空指针,假如在 System.out.println(multiThreadError6.states.get("1"));
之前 加入 Thread.sleep(1000)
休眠一段时间后等另外一个线程执行完,就不会出现这个问题了。
三、总结
1. 各种需要考虑线程安全的情况,如下:
访问共享的变量或资源,会有并发风险,这里的共享变量或资源指的是:对象的属性,静态变量,共享缓存,数据库等等。
所有依赖时序的操作,即可以拆分成多个步骤的操作,即使每一步操作都是线程安全的,但是如果存在操作时序不对,还是存在并发问题,比如:read-modify-write(先读取再修改最后写入)、 check-then-act(先检查再操作)
不同的数据之间存在捆绑关系的时候,那就要么把这些捆绑的数据全部修改,要么都不修改
在使用其他类的时候,如果该类没有声明自己是线程安全的,那就要注意该类可能是线程不安全的
2. 多线程除了安全问题,还可能会导致性能问题:
从某种程度上来讲,多线程可以提高复杂的运算效率,但是一定程度上多线程可能会带来性能提交,比如多线程间的调度和协作带来的性能开销。
(1)调度:上下文切换
线程运行个数超过CPU核心数的时候,CPU就需要对线程进行调度,线程调度中就涉及线程切换,线程的切换的开销是很大的,CPU需要保存当前线程的运行场景,将当前线程的当前运行状态保存好,为载入新的运行线程做准备。这样来来回回其实是很耗费性能的。而引起密集的上下文切换的操作就包括抢锁和IO操作。
(2)协作:内存同步
多个线程之间,针对数据的同步其实大部分是基于 JMM 模型的,这种需要我们后续详细学习并总结,这里只是需要知道,多个线程之间,同步数据也是多线程消耗性能的一个原因。
文章来源:多线程安全的案例展示与解决方案
个人微信:CaiBaoDeCai
微信公众号名称:Java知者
微信公众号 ID: JavaZhiZhe
谢谢关注!
关键词:
-
【天天新要闻】读改变未来的九大算法笔记02_数据库
![](https: img2023 cnblogs com blog 3076680 202306 3076680-20230602231102469-1389179464 png) 1 & 16
来源: 每日快讯!北京西城区举办建筑工地防汛抢险应急救援演练
印度列车相撞事故已致120死超800伤 车头被撞扁:现场惨烈
今日热文:椰树集团首次回应直播风格争议:审美回归、主打真实自然
【天天新要闻】读改变未来的九大算法笔记02_数据库
九八年属什么(中国与十二地支相配以人出生年份的十二种动物)-即时焦点
新动态:当在浏览器中输入百度地址后,发生了什么?(计算机网络篇)
ASP.NET Core优雅的添加HealthCheck_快看点
第六章:分区_每日热点
VX自动刷步数脚本
Visual Studio如何使用自带“诊断工具” 世界微头条
RCEP对15个签署国全面生效|天天消息
比过山车刺激多了 女子体验菲律宾360度秋千:全程尖叫 每日快播
世界滚动:AMD显卡两大神技宣布半年了:还都是PPT!
全世界最大盗版网站死了!居然还和俄乌冲突有关-环球微资讯
IGN满分的神作终于出中文了!可我却高兴不起来
迪士尼公主电影真人与动画对比 你最喜欢哪一个?
左蓝微电子技术有限公司_左蓝-世界快报
世界速讯:下周市场的风险在哪里?
千兆网线接线定义标准_千兆网线做法图解 天天观察
蚌埠到南京的火车票时刻表(火车票时刻表)
南京启动江心洲生态岛试验区创建 通讯
《新型电力系统发展蓝皮书》发布:提出储能规模化布局应用等四大体系 焦点播报
云集(YJ.US)Q1净亏损同比收窄 盘前飙涨110%_当前热文
疑因节省开支 马斯克取消推特的“翻译”功能
峰学蔚来公司每周做四休三 为考研名师张雪峰规定
黑人女子不满遭解雇 向法院提起诉讼称涉嫌种族和性别歧视
B站正式会员数达到2.05亿 其日均活跃用户数为9370万
泸州老窖曝光全专利 为可制备粉末白酒旨在方便储存及运输
苹果首款无风扇笔记本被列为过时产品 发售仅有8年
为进步加强新兴技术领域发展 韩国将投资约5000亿推进其AI项目发展
出于安全考虑 尼康Z8将禁用第三方电池
江苏一学生网购巴掌块小石头作画 商家发来一块7斤重大石头
东海租赁拟向东营茂阳新能源开发有限公司借款4500万 借款利率6%-世界微速讯
ASP.NET Core MVC 从入门到精通之自动映射(一) 环球热闻
史上最贵“蹦迪票”!神秘买家8万拍下椰树直播间蹦迪权:1秒单价2698元
【环球时快讯】学习笔记——VMWare的使用和Windows的安装
【环球报资讯】鬼谷子劝世良言篇
Celery框架
19)事务 天天即时
我的第一个项目(十五) :完成数据保存功能(后端,改update)_当前关注
每日快讯!这个“六一”我们让爱与欢乐“童”行|咸宁市第十七小学开展首届艺术节活动
女孩发视频吐槽房东 称其安装5级能耗导致电费超标
“黄晓明救了我的手机”冲上微博热搜 感觉不如昆仑玻璃
69岁!赵雅芝晒新写真 气质优雅梦回白娘子:网友感慨这谁顶得住 环球速递
5月新能源销量分析:比亚迪“冠军”初显威 江湖再无“蔚小理”-世界头条
青春永驻 还能多活十年:天下真有如此好事?
泪目!老伴走后第二天浙大老教师又去捡垃圾了:为资助贫困学生
天天看点:美团单车怎么关锁还车视频(美团单车怎么关锁还车)
文心一言 VS 讯飞星火 VS chatgpt (29)-- 算法导论5.2 1题
世界速讯:人民银行营管部发文加强征信合规管理 要求对高发征信合规问题开展自查和整改
全球速看:身轻如燕玩光追 1080P甜品卡就选它!耕升RTX 4060 Ti踏雪显卡评测
每日热点:3D扫描效率剧增!NVIDIA新AI模型可将视频生成高精模型
实时焦点:vivo X100系列首发天玑9300:超大杯搭载骁龙8 Gen3
三地同时遭遇太罕见!专家谈辽宁龙卷风:持续30分钟较少见 世界讯息
支付宝“史诗级”更新!终于支持深色模式了:3步教你开启
真崎杏子r18_真崎杏子
linux 性能自我学习 ———— 不可中断进程和僵尸进程 [四]-今日热议
环球要闻:上交所:6月5日上市交易科创50ETF期权 涨跌幅参数适应性调整为20%
一辆最多2万元!多地发放购车补贴,6月买车真是好时机?_当前热门
商业力+公益心 正大广场K3H四周年童趣升级-今日关注
为求效率最大化:AI测试中将矛头指向人类操作者|每日简讯
司机倒车坠河万分紧急 路人一个个冲了过来:3分钟救起落水女子
一文了解摩尔线程MTT S70显卡!国产显卡最新大作-环球通讯
又一车企面临退市!市值蒸发98% 美国新造车也是一地鸡毛
深圳一公司员工因上完厕所不冲水被开除 网友一边倒支持:活该被开
学习笔记——软件、软件分类、软件测试、数据的形式与数制(进制之间的转化)_全球即时看
观速讯丨总结vue3 的一些知识点:Vue.js 条件语句
总结vue3 的一些知识点:Vue.js 安装
焦点资讯:网红经济学家转型带货 任泽平开卖生发油:曾炮轰苹果割韭菜
三星Galaxy S23系列再出新问题:这次是相机拍照模糊
每日消息!经济学家转型带货!任泽平卖生发油和酱香白酒
开车上高速竟让婴儿平躺副驾驶!理想汽车回应:非常危险 坚决反对|环球新视野
热点!5699元供不应求!moto razr 40 Ultra预售被抢购一空
海博早读|福建130多万中学生正面临这种健康危害
云原生第五周--k8s实战案例
环球快讯:ChatGPT 背后的英雄——AI芯片
世界快播:记录--让URL地址都变成了"ooooooooo"
利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)
Angular Google Charts教程_编程入门自学教程_菜鸟教程-免费教程分享 全球即时看
【天天新视野】财政系统组织申报2023年第二批专项债项目:13领域可用专项债作资本金
看点:商品日报(6月2日):市场风险偏好继续改善 玻璃涨停沪镍纯碱涨超4%
天天即时:《封神三部曲》首部即将上映:制作成本或为16.5亿元
如何拍出女朋友最美一面?OPPO Reno10 Pro+评测:长焦人像让直男也会拍照
今日关注:不到1斤重!宏碁蜂鸟迷你主机开卖:1499元起
安全带又出现问题!韩系豪华品牌捷尼赛思宣布召回G80、GV70等共计1602辆|环球最新
比亚迪西安工厂起火 现场浓烟滚滚!官方回应
当前短讯!什么可以防电脑辐射_什么防辐射
又双叒叕种草了新家装风格?AI帮你家居换装-全球今头条
vul -- Cybero靶机渗透
各大建站方案
今日快看!【解决方法】锐捷路由器配置IP地址,如RSR路由器
失业率下降影响市场甚微 短端日债延续走强
全球短讯!小鹏“掉队”,它究竟做错了什么?
单日票房占比超50%!《蜘蛛侠:纵横宇宙》评分解禁:9.0超高分
又一“巨无霸”!国产2万吨重载自动驾驶列车试验成功
2.9秒下完一部电影!Wi-Fi 7要来了:国内标准已落地
moto razr 40 ultra为何要装一块超大尺寸外屏?看完明白了
中国最深高铁站感受下:仅扶梯垂直高度42米 相当于15层楼 每日动态
2023.06 微信抓包方案 · 亲测可用
ASP.Net Core 刷新界面,内存持续增加不释放问题。