当前位置: 首页 > news >正文

JAVA小知识30:JAVA多线程篇1,认识多线程与线程安全问题以及解决方案。(万字解析)

来        多线程,一个学起来挺难但是实际应用不难的一个知识点,甚至在很多情况下都不需要考虑,最多就是写测试类的时候模拟一下并发,现在我们就来讲讲基础的多线程知识。

一、线程和进程、并发与并行

1.1、线程和进程

线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。线程是一个进程中的实际执行单位,它负责当前进程中程序的执行。在一个进程中可以有多个线程,这些线程可以共享进程的资源,如堆和方法区。然而,每个线程都有自己的程序计数器、虚拟机栈和本地方法栈。因此,系统在产生一个线程或在不同线程间切换时的负担要小于进程,这也使得线程被称为轻量级进程

进程:进程是程序的基本执行实体。一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

1.2、并发与并行

并发:在同一时刻,有多个指令在单个CPU上交替执行 。
并行:在同一时刻,有多个指令在多个CPU上同时执行

二、实现多线程的三种方法 

2.1 继承Thread

多线程的第一种启动方式:

  1. 自己定义一个类继承Thread
  2. 重写run方法
    1. public class Student extends Thread{@Overridepublic void run() {// getName():获取当前线程名字for(int i=0;i<200;i++) {System.out.println("我是学生"+getName());}}
      }
  3. 创建子类的对象,并启动线程
    1. @Test
      void threadtest1(){Student s1 = new Student();Student s2 = new Student();s1.setName("1");//设置线程名s2.setName("2");//设置线程名s1.start(); //启动线程用start 而不是调用run方法s2.start();
      }

来看结果:

2.2、实现Runnable接口

 多线程的第二种启动方式:

  1. 自己定义一个类实现Runnable接口
  2. 重写里面的run方法
    1. public class Student implements Runnable{@Overridepublic void run() {// getName():获取当前线程名字for(int i=0;i<200;i++) {//Thread.currentThread():获取当前执行该线程的线程对象Thread thread = Thread.currentThread();System.out.println("我是老师"+thread.getName());}}
      }
  3. 创建自己的类的对象
  4. 创建一个Thread类的对象,并开启线程
    1. @Test
      void threadtest2(){Student s1 = new Student();Thread t1= new Thread(s1);Thread t2= new Thread(s1);t1.setName("1");t2.setName("2");t1.start();t2.start();
      }

我们来看结果:老师1与老师2交替执行

2.3、Callable

特点:可以获取到多线程运行的结果

  1. 创建一个类实现callable接口
  2. 重写call (是有返回值的。表示多线程运行的结果)
    1. public class Student implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1-100的合int sum=0;for(int i=0;i<100;i++){sum+=i;}return sum;}
      }
  3. 创建类的对象(表示多线程要执行的任务)
  4. 创建Futureask的对象(作用:管理多线程运行的结果)
  5. 创建Thread类的对象,并启动(表示线程)
    1. void threadtest3() throws ExecutionException, InterruptedException {// 创建类的对象(表示多线程要执行的任务)Student s1 = new Student();// 创建Futureask的对象(作用管理多线程运行的结果)FutureTask<Integer> f1 = new FutureTask<Integer>(s1);// 创建Thread类的对象,并启动(表示线程)Thread t1 = new Thread(f1);t1.start();// 获取线程执行结果Integer i1 = f1.get();System.out.println(i1);
      }

答案为:5050 

看一下三种方法的优缺点

三、Thread中常用的成员方法

3.1、成员方法示例

3.1.1 get与set
public static void main(String[] args){Demo1 demo1 = new Demo1();Demo1 demo2 = new Demo1("设置了名字的线程2");Demo1 demo3 = new Demo1();demo3.setName("设置了名字的线程3");demo1.start();demo2.start();demo3.start();
}
public class Demo1 extends Thread{public Demo1(String name) {super(name);}public Demo1() {}@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + "@" + i);}}
}

3.1.2 currentThread()
public static void main(String[] args){Thread thread = Thread.currentThread();System.out.println(thread.getName());//main
}
3.1.3 getPriority()与setPriority()

线程分为10档 最小为1 最大为10 默认就是5吗,优先级不是绝对的 他是一个概率问题。

public static void main(String[] args){Thread thread = Thread.currentThread();System.out.println(thread.getPriority());//5
}
public static void main(String[] args){Thread thread = Thread.currentThread();thread.setPriority(10);System.out.println(thread.getPriority());//10
}
3.1.4 setDaemon()守护线程

守护线程就是:当非守护线程执行完毕之后,守护线程也会陆陆续续的停止,无论是否执行完毕,但是不会马上停止,会有个过程。

public class Demo1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + "@" + i);}}
}
public class Demo2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(getName() + "@" + i);}}
}
public static void main(String[] args){Demo1 demo1 = new Demo1();Demo2 demo2 = new Demo2();demo1.setName("非守护线程");demo2.setName("守护线程");demo2.setDaemon(true);demo1.start();demo2.start();
}

这里的Demo2对象被设定为守护线程,他原本要执行1w次的,现在执行结果。

非守护线程执行结束之后,他只执行到了20次就结束了。

四、线程的生命周期

五、线程安全问题与解决方案

5.1 线程安全问题的发生

现在有一个订单秒杀,总共是100份,分三个平台来卖,我们看一下代码:

public class Goods extends Thread {// 加static就代表类对象共用一个countstatic int count=0;@Overridepublic void run() {while (count<100){count++;System.out.println(getName()+"正在卖第"+count+"个商品");}}
}
public static void main(String[] args) {Goods good1 = new Goods();Goods good2 = new Goods();Goods good3 = new Goods();good1.setName("某宝");good2.setName("某东");good3.setName("某多多");good1.start();good2.start();good3.start();
}

看结果:很明显,有问题,三家商城共卖一个?这能对吗,这肯定是不对的。这种问题就是线程并发的安全问题。

其包括有:
        1. 不同商铺卖同一个商品问题

        2. 超卖问题,也就是只有100个商品却卖了103个的问题

 5.2 同步代码块

把操作共享数据的代码锁起来:

特点1:锁默认打开,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开

我们接下来看看通过同步代码块修改之后的代码:

// 加static就代表类对象共用一个count
static int count=0;
// 锁对象 锁对象非常的随意 但是切记需要唯一
static Object object = new Object();
@Override
public void run() {while (true){synchronized (object) {if(count<1000) {count++;System.out.println(getName() + "正在卖第" + count + "个商品");}else {break;}}}
}
public static void main(String[] args) {Goods good1 = new Goods();Goods good2 = new Goods();Goods good3 = new Goods();good1.setName("某宝");good2.setName("某东");good3.setName("某多多");good1.start();good2.start();good3.start();
}

既没有超卖问题,也没用同一个店铺卖同一个商品的问题 


 

5.3 同步方法 

就是把synchronized关键字加到方法上。

特点1:同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定:
        非静态:this
        静态:当前类的字节码文件对象

public class Goods extends Thread {
// 加static就代表类对象共用一个countstatic int count=0;// 锁对象 锁对象非常的随意 但是切记需要唯一static Object object = new Object();@Overridepublic void run() {while (true){if (extracted()) {break;}}}private synchronized boolean extracted() {if(count==1000) {return true;}else {count++;System.out.println(Thread.currentThread().getName() + "正在卖第" + count + "个商品");}return false;}
}
public static void main(String[] args) {Goods gd = new Goods();Thread good1 = new Thread(gd);Thread good2 = new Thread(gd);Thread good3 = new Thread(gd);good1.setName("某宝");good2.setName("某东");good3.setName("某多多");good1.start();good2.start();good3.start();
}

5.4 Lock

        虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁。
        为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

Lock中提供了获得锁和释放锁的方法
        1. void lock():获得锁
        2. void unlock():释放锁

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法

ReentrantLock():创建一个ReentrantLock的实例。

注意:

在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。
说明一:如果在lock方法与try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。
说明二:如果lock方法在try代码块之内,可能由于其它方法抛出异常,导致在finally代码块中,unlock对未加锁的对象解锁,它会调用AQS的tryRelease方法(取决于具体实现类),抛出IllegalMonitorStateException异常。
说明三:在Lock对象的lock方法实现中可能抛出unchecked异常,产生的后果与说明二相同。 java.concurrent.LockShouldWithTryFinallyRule.rule.desc
            

Positive example:Lock lock = new XxxLock();// ...lock.lock();try {doSomething();doOthers();} finally {lock.unlock();}

Negative example:Lock lock = new XxxLock();// ...try {// If an exception is thrown here, the finally block is executed directlydoSomething();// The finally block executes regardless of whether the lock is successful or notlock.lock();doOthers();} finally {lock.unlock();}

来看例子:

这里要注意的点是,首先 static Lock lock = new ReentrantLock();锁要加上static作为唯一的锁,第二是释放锁要在finally代码块中。

public class Teacher {public static void main(String[] args) {Student s1 = new Student();Student s2 = new Student();s1.setName("王老师");s2.setName("张老师");s1.start();s2.start();}
}
// 第二个类
public class Student extends Thread {static int studentID = 0;static Object o1 = new Object();static Lock lock = new ReentrantLock();@Overridepublic void run() {while (true) {lock.lock();try {if (studentID < 100) {sleep(50);studentID++;System.out.println(getName() + "调用学号为:" + studentID + "的学生去干活");} else {break;}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}

 检查过输出之后,无超过100,无重复调用

六、死锁

我们上个章节讲到了线程安全问题以及解决方案,大体的解决方案就是加锁,那么加锁虽然会解决多线程并发的安全问题,同时也会造成一个新的问题:死锁

什么是死锁呢?打个比方:

两个人在一条窄窄的单行道上相向而行,每个人都在等待对方让路,以便自己能够通过。但因为两人都固执地站着不动,等待对方先让步,结果就是谁也无法前进,造成了一种僵持不下的局面。

在这个例子中,两个人就像是计算机中的两个进程,而这条单行道就像是计算机系统中的资源。如果每个进程都持有一个资源(比如一个人占据了道路的一部分),并且同时等待另一个进程释放它所持有的资源(另一个人占据的部分),而对方也在做同样的事情,那么双方就会陷入死锁状态,除非外部干预,否则他们都无法继续前进。

死锁并不是一个知识点,他是一个错误,需要我们在开发中避免。

那么死锁一般都是怎么发生的呢?

正常来讲一般都是锁里面套另外一个锁,会发生死锁现象 !

public class main {public class DeadlockExample {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Locked lock1");try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }synchronized (lock2) {System.out.println("Thread 1: Locked lock2");}}});Thread t2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Locked lock2");try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }synchronized (lock1) {System.out.println("Thread 2: Locked lock1");}}});t1.start();t2.start();}}
}

在这个例子中,我们有两个线程t1t2,它们分别尝试以不同的顺序锁定两个资源resource1resource2。每个线程首先锁定一个资源,然后稍作等待(模拟一些工作),接着尝试锁定另一个资源。

  • 线程t1首先锁定resource1,然后尝试锁定resource2
  • 线程t2首先锁定resource2,然后尝试锁定resource1

如果线程t1在线程t2锁定resource2之前锁定了resource1,并且线程t2在同一时间锁定了resource2,那么它们都会等待对方释放资源,从而形成死锁。每个线程都持有一个资源并且等待另一个线程释放它需要的资源,但没有线程能够继续执行,因为所需的资源被对方持有。

相关文章:

JAVA小知识30:JAVA多线程篇1,认识多线程与线程安全问题以及解决方案。(万字解析)

来 多线程&#xff0c;一个学起来挺难但是实际应用不难的一个知识点&#xff0c;甚至在很多情况下都不需要考虑&#xff0c;最多就是写测试类的时候模拟一下并发&#xff0c;现在我们就来讲讲基础的多线程知识。 一、线程和进程、并发与并行 1.1、线程和进程 线程&am…...

Python数据分析案例47——笔记本电脑价格影响因素分析

案例背景 博主对电脑的价格和配置一直略有研究&#xff0c;正好最近也有笔记本电脑相关的数据&#xff0c;想着来做点分析吧&#xff0c;写成一个案例。基本上描述性统计&#xff0c;画图&#xff0c;分组聚合&#xff0c;机器学习&#xff0c;交叉验证&#xff0c;搜索超参数…...

【加密与解密】【09】GPG Client签名流程

什么是GPG客户端 GPG客户端是实现PGP加密协议的一套客户端程序&#xff0c;可用于加密或签名 下载GPG客户端 建议安装命令行工具&#xff0c;图形工具一般不具备完整功能 https://gnupg.org/download/index.html生成私钥 此时会要求你输入名称&#xff0c;邮箱&#xff0c…...

“2024软博会” 为软件企业提供集展示、交流、合作一站式平台

随着全球科技浪潮的涌动&#xff0c;软件行业正迎来前所未有的发展机遇&#xff0c;成为了全球新一轮竞争的“制高点”&#xff0c;以及未来经济发展的“增长点”。在当前互联网、大数据、云计算、人工智能、区块链等技术加速创新的背景下&#xff0c;数字经济已经渗透到经济社…...

【Zoom安全解析】深入Zoom的端到端加密机制

标题&#xff1a;【Zoom安全解析】深入Zoom的端到端加密机制 在远程工作和在线会议变得越来越普及的今天&#xff0c;视频会议平台的安全性成为了用户关注的焦点。Zoom作为全球领先的视频会议软件&#xff0c;其端到端加密&#xff08;E2EE&#xff09;功能保证了通话的安全性…...

7 动态规划

下面的例子不错&#xff1a; 对于动态规划&#xff0c;能学到不少东西&#xff1b; 你要清楚每一步都在做什么&#xff0c;划分细致就能够拆解清楚&#xff01; xk​​​​​​​. - 力扣&#xff08;LeetCode&#xff09; labuladong的算法笔记-动态规划-CSDN博客 动态规划是…...

.net 快速开发框架开源

DF.OpenAPI开源系统 前后端分离&#xff0c;开箱即用&#xff0c;java经典功能.net也具备 系统介绍 DF.OpenAPI是基于Admin.NET二开的&#xff0c;是一个开源的多租户后台管理系统。采用前后端分离技术&#xff08;前端使用vue.js&#xff0c;后端使用.net 3~.net6&#xff…...

《昇思25天学习打卡营第06天|网络构建》

网络构建 神经网络模型由神经网络层和Tensor操作构成 #实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninstall mindspore -y !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2.…...

【链表】- 两两交换链表中的节点

1. 对应力扣题目连接 两两交换链表中的节点 2. 实现案例代码 public class ExchangeLinkedListsPairwise {public static void main(String[] args) {// 示例链表&#xff1a;[1, 2, 3, 4]ListNode head new ListNode(1);head.next new ListNode(2);head.next.next new L…...

java设计模式(四)——抽象工厂模式

一、模式介绍 改善在工厂方法模式中,扩展时新增产品类、工厂类,导致项目中类巨多的场面,减少系统的维护成本,且一个工厂可以生成多种产品,而不是同一种的产品,比如一个工厂既可以生产鞋子又可以衣服,而不是只能生产鞋子。 二、工厂方法模式 1、实现步骤 第一步: 定义…...

动物检测yolo格式数据集(水牛 、大象 、犀牛 、斑马四类)

动物检测数据集 1、下载地址&#xff1a; https://download.csdn.net/download/qq_15060477/89512588?spm1001.2101.3001.9500 2、数据集介绍 本数据集含有四种动物可以检测&#xff0c;分别是水牛 、大象 、犀牛 、斑马四类&#xff0c;数据集格式为yolo格式&#xff0c;…...

昇思25天学习打卡营第05天 | 数据变换 Transforms

昇思25天学习打卡营第05天 | 数据变换 Transforms 文章目录 昇思25天学习打卡营第05天 | 数据变换 TransformsCommon TransformsCompose Vision TransformsText TransformPythonTokenizerLookup Lambda Transforms数据处理模式Pipeline模式Eager模式 总结打卡 通常情况下的原始…...

Springboot+MySQL 公寓报修管理系统源码

功能结构图 效果图&#xff1a;...

jenkins 发布服务到linux服务器

1.环境准备 1.1 需要一台已经部署了jenkins的服务器&#xff0c;上面已经集成好了&#xff0c;jdk、maven、nodejs、git等基础的服务。 1.2 需要安装插件 pusblish over ssh 1.3 准备一台额外的linux服务器&#xff0c;安装好jdk 2.流程描述 2.1 配置jenkins&#xff0c;包括p…...

Appium+python自动化(三十九)-Appium自动化测试框架综合实践 - 代码实现(超详解)

1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现。由于时间的关系&#xff0c;宏哥这里用代码给小伙伴演示两个模块&#xff1a;注册和登录。 2.业务模块封装 因为现在各种APP的层出不群&#xff0c;各式各样的。但是其大多数都有注册、登录。为…...

防止跨站脚本攻击XSS之Antisamy

目录 一、什么是跨站脚本攻击&#xff08;XSS&#xff09; 二、通常有哪些解决方案 三、常见的XSS攻击例子有哪些 3.1 存储型XSS攻击&#xff08;黑产恶意截流&#xff0c;跳转不法网站&#xff09; 3.2反射型XSS攻击&#xff1a; 四、什么是跨站请求伪造&#xff1f; 五…...

Python爬虫实战案例——王者荣耀皮肤抓取

大家好&#xff0c;我是你们的老朋友——南枫&#xff0c;今天我们一起来学习一下该如何抓取大家经常玩的游戏——王者荣耀里面的所有英雄的皮肤。 老规矩&#xff0c;直接上代码&#xff1a; 导入我们需要使用到的&#xff0c;也是唯一用到的库&#xff1a; 我们要抓取皮肤其…...

PyTorch计算机视觉实战:目标检测、图像处理与深度学习

本书基于真实数据集&#xff0c;全面系统地阐述现代计算机视觉实用技术、方法和实践&#xff0c;涵盖50多个计算机视觉问题。全书分为四部分&#xff1a;一部分介绍神经网络和PyTorch的基础知识&#xff0c;以及如何使用PyTorch构建并训练神经网络&#xff0c;包括输入数据缩放…...

4D 生物打印:将时间维度融入,打造个性化动态组织

4D 生物打印技术将时间维度融入 3D 生物打印&#xff0c;赋予打印出的结构动态变化的能力&#xff0c;使其更接近于真实组织和器官的特性。要实现这一目标&#xff0c;需要使用智能生物材料和智能设计策略。 智能生物材料 目前用于 4D 生物打印的智能生物材料主要包括形状记忆…...

银行清算业务功能测试解析

银行清算业务是指银行间通过账户或有关货币当地清算系统&#xff0c;在办理结算和支付中用以清讫双边或多边债权债务的过程和方法。按地域划分&#xff0c;清算业务可分为国内联行清算和国际清算。常见的清算模式包括实时全额清算、净额批量清算、大额资金转账系统及小额定时清…...

CVE-2024-6387漏洞预警:尽快升级OpenSSH

OpenSSH维护者发布了安全更新&#xff0c;其中包含一个严重的安全漏洞&#xff0c;该漏洞可能导致在基于glibc的Linux系统中使用root权限执行未经身份验证的远程代码。该漏洞的代号为regreSSHion&#xff0c;CVE标识符为CVE-2024-6387。它驻留在OpenSSH服务器组件&#xff08;也…...

学习整理在php中使用PHPExcel读取excel表列数大于Z时读取不到的解决方案

php读取excel列数大于Z时读取不到 背景解决方案关键代码 背景 表格数据超过26列&#xff0c; 也就是在Z列之前没有AA列及以后的情况&#xff0c; 测试一直都没有问题&#xff0c;超过&#xff0c;就会获取不到数据了 解决方案 private function getExcelData(){//获取excel文…...

python sklearn机械学习-数据预处理

&#x1f308;所属专栏&#xff1a;【机械学习】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您…...

搜索引擎常用语法

引号 (" "): 用双引号将词组括起来&#xff0c;搜索引擎将返回包含完全相同短语的结果。 示例&#xff1a;"人工智能发展趋势" 减号 (-): 在关键词前加上减号可以排除包含特定词语的结果。 示例&#xff1a;人工智能 -机器学习&#xff08;排除包含 “机器…...

华为智能驾驶方案剖析

华为ADS智驾方案始终坚持激光雷达毫米波雷达摄像头的多传感器融合路线&#xff0c;行业降本压力下硬件配置从超配逐步转向贴合实际需求&#xff0c;带动整体硬件成本下降。 1)单车传感器数量呈现下降趋势&#xff0c;包括激光雷达从3个减配至1个、毫米波雷达从6R减配至3R、摄像…...

DDR3(一)

目录 1 SDRAM1.1 同步动态随机存储器1.2 位宽1.3 SDRAM结构1.4 SDRAM引脚图 2 SDRAM操作指令2.1 读写指令2.2 刷新和预充电2.3 配置模式寄存器2.4 读/写突发2.5 数据屏蔽 SDRAM是DDR3的基础&#xff0c;在学习DDR3之前&#xff0c;我们先来学习一下SDRAM的相关知识。 1 SDRAM …...

Eureka与Spring Cloud Bus的协同:打造智能服务发现新篇章

Eureka与Spring Cloud Bus的协同&#xff1a;打造智能服务发现新篇章 在微服务架构中&#xff0c;服务发现是实现服务间通信的关键机制。Eureka作为Netflix开源的服务发现框架&#xff0c;与Spring Cloud Bus的集成&#xff0c;提供了一种动态、响应式的服务治理解决方案。本文…...

Kafka入门到精通(三)-Kafka

Kafka简介 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。 这种动作&#xff08;网页浏览&#xff0c;搜索和其他用户的行动&#xf…...

高校教师教学质量评估系统-计算机毕业设计源码03344

摘要 在高等教育中&#xff0c;教学质量是培养优秀人才的关键。为了提高教学质量&#xff0c;高校需要建立一套科学、有效的教师教学质量评估系统。本研究采用 SSM技术框架&#xff0c;旨在开发一款高校教师教学质量评估系统。 SSM框架作为一种成熟的Java开发框架&#xff0c;具…...

币界网讯,预计以太坊现货 ETF 将于 7 月中旬推出

刚刚 ETF Store 总裁 Nate Geraci 在 X &#xff08;前Twitter&#xff09;平台上宣布&#xff0c;备受数字货币市场期待的SEC以太坊现货 ETF提案&#xff0c;将于7 月中旬通过美国证券交易委员会&#xff08;SEC&#xff09;批准。Nate Geraci透露修订后的 S-1 文件将于 7 月 …...