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

【并发基础】Java中线程的创建和运行以及相关源码分析

目录

一、线程的创建和运行

1.1 创建和运行线程的三种方法

1.2 三者之间的继承关系

二、Thread类和Runnable接口的区别

2.1 Runnable接口可以实现线程之间资源共享,而Thread类不能

2.2 实现Runnable接口相对于继承Thread类的优点

三、实现 Runnable 接口和实现 Callable 接口的区别

四、Thread类和Runnable接口关于启动线程的源码解析

4.1 实现方法

4.2 Thread.start()方法源码分析

4.3 Runnable.run()方法源码分析

4.4 总结


一、线程的创建和运行

1.1 创建和运行线程的三种方法

Java里的程序天生就是多线程的,那么有几种启动线程的方式? 

Java 线程创建有3种方式:

  1. 继承 Thread 类并且重写 run 方法
  2. 实现 Runnable接口的 run 方法
  3. 使用 Callable接口和FutureTask类方式

1.2 三者之间的继承关系

public class Thread implements Runnable {}

Thread类也是实现的Runnable接口。

单独说下 FutureTask 的方式,这种方式的本身也是实现了Runnable 接口的 run 方法,看它的继承结构就可以知道。

前两种方式都没办法拿到任务的返回结果,但是 Futuretask 方式可以。

由此我们知道了,其实这三种创建方法的根源,都是来源于Runnable接口,这三种方法往上层追溯都能追到Runnble接口。

二、Thread类和Runnable接口的区别

2.1 Runnable接口可以实现线程之间资源共享,而Thread类不能

实际上Thread类和Runnable接口之间在使用上也是有所区别的,如果一个类继承Thread类,就不适合于多个线程共享资源,而实现了Runnable接口,则可以方便的实现资源的共享。

由上文我们就可以知道,Thread类和Runnable接口最大的区别就是继承Thread类不能资源共享,而实现Runnable接口可以资源共享。 

为什么Runnable可以共享数据:

总结起来原因就是用Runnable接口的方法可以对两个不同的Thread类的构造方法传入相同的实现Runnable接口的对象,那么这两个不同的Thread线程类本质操控的是同一个Runnable接口的实现对象了,调用的也是同一个run()方法,自然这两个线程下就实现了共享同一个Runnable实现类中的数据了。

如果两个Thread类的构造方法传入不同的Runnable接口实现类,那么两个Thread线程对象操作的不是同一个Runnable实现类,两个线程也就不能共享数据了。

2.2 实现Runnable接口相对于继承Thread类的优点

可见,实现Runnable接口相对于继承Thread类来说,有如下显著的优势:

  • 适合多个相同程序代码的线程去处理同一资源的情况。
  • 可以避免由于Java的单继承特性带来的局限。
  • 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
  • 线程池只能放入实现 Runable 或 Callable 类线程,不能直接放入继承 Thread 的类

三、实现 Runnable 接口和实现 Callable 接口的区别

  1. Runnable 是自从 java1.1 就有了,而 Callable 是 1.5 之后才加上去的
  2. 实现 Callable 接口的任务线程能返回执行结果,而实现 Runnable 接口的任务线程不能返回结果
  3. Callable 接口的 call()方法允许抛出异常,而 Runnable 接口的 run()方法的异常只能在内部消化,不能继续上抛
  4. 加入线程池运行,Runnable 使用 ExecutorService 的 execute 方法,Callable 使用 submit 方法。注:Callable 接口支持返回执行结果,此时需要调用 FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

四、Thread类和Runnable接口关于启动线程的源码解析

这里我们来讲一下Java中创建线程最经典的这两种方式,在底层源码是如何实现的。

4.1 实现方法

Java 中实现多线程有两种「基本方式」:继承 Thread 类和实现 Runnable 接口。从实现的编程手法来看,认为这是两种实现方式并无不妥。但是究其实现根源,这么讲其实并不准确。

其实多线程从根本上讲只有一种实现方式,就是实例化 Thread,并且提供其执行的 run 方法。无论你是通过继承 Thread还是实现 Runnable接口,最终都是重写或者实现了 run 方法。而你真正启动线程都是通过实例化 Thread,调用其 start 方法

来看下两种不同实现方式的例子:

1. 继承 Thread 方式:

public class MyThread extends Thread {  public void run() {  System.out.println("MyThread.run()");  }  
}  
MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();  

2. 实现 Runnable 方式

public class MyThread extends OtherClass implements Runnable {  public void run() {  System.out.println("MyThread.run()");  }  
} 
MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start(); 

第一种方式中,MyThread 继承了 Thread 类,启动时调用的 start 方法,其实还是他父类 Thread 的 start 方法。并最终触发执行 Student 重写的 run 方法。

第二种方式中,MyThread 实现 Runnable 接口,将MyThread对象作为参数传递给 Thread 构造函数。接下来还是调用了 Thread 的 start 方法。最后则会触发传入的 Runnable 实现类的 run 方法。

两种方式都是创建 Thread 或者 Thread 的子类,通过 Thread 的 start 方法启动。唯一不同是第一种 run 方法实现在 Thread 子类中。第二种则是把 run 方法逻辑转移到 Runnable 的实现类中。线程启动后,第一种方式是 thread 对象运行自己的 run 方法逻辑,第二种方式则是调用 Runnable 实现的 run 方法逻辑。

相比较来说,第二种方式是更好的实践,原因如下:

  1. java 语言中只能单继承,通过实现接口的方式,可以让实现类去继承其它类。而直接继承 thread 就不能再继承其它类了;
  2. 线程控制逻辑在 Thread 类中,业务运行逻辑在 Runnable 实现类中。解耦更为彻底;
  3. 实现 Runnable 的实例,可以被多个线程共享并执行。而实现 thread 是做不到这一点的。

看到这里,你是不是很好奇,为什么程序中调用的是 Thread 的 start 方法,而不是 run 方法?为什么线程在调用 start 方法后会执行 run 方法的逻辑呢?接下来我们通过学习 start 方法的源代码来找到答案。

4.2 Thread.start()方法源码分析

Thread类的无参构造方法:

public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);
}

如果是直接创建Thread类对象,我们通过源码就能看出,传入到target是空。在这种情况下,我们需要在Thread的继承类中去覆写run()方法,这样在Thread类执行run()方法的时候,就是调用我们继承类中覆写的run()方法逻辑。

我们知道Thraed类的对象是不能直接调用run()方法的,那么它是如何调用run()方法的呢?下面我们接着来进行分析。

Thread类中start()方法源码:

public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();    group.add(this);boolean started = false;try {        start0();        started = true;    } finally {try {if (!started) {                group.threadStartFailed(this);            }        } catch (Throwable ignore) {        }    }
}

这段代码足够简单,简单到没什么内容。主要逻辑如下:

  1. 检查线程的状态,是否可以启动;
  2. 把线程加入到线程 group 中;
  3. 调用了 start0 () 方法。

可以看到 Start 方法中最终调用的是 start0()方法,并不是 run 方法。那么我们再看 start0 方法源代码:

private native void start0();

什么也没有,因为 start0 是一个 native 方法,也称为 JNI(Java Native Interface)方法。JNI 方法是 Java和其它语言交互的方式。同样也是 Java代码和虚拟机交互的方式,虚拟机就是由 C++ 和汇编所编写。

由于 start0 是一个 native 方法,所以后面的执行会进入到 JVM 中。那么 run 方法到底是何时被调用的呢?这里似乎找不到答案了。

难道我们错过了什么?回过头来我们再看看 Start 方法的注解。其实读源代码的时候,要先读注解,否则直接进入代码逻辑,容易陷进去,出不来。原来答案就在 start 方法的注解里,我们可以看到:

/* 
* Causes this thread to begin execution; the Java Virtual Machine* calls the run method of this thread.* 
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start method) and the other thread (which executes its
* run method).
*
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed execution.
*/

最关键一句: the Java Virtual Machine calls the run method of this thread。由此我们可以推断出整个执行流程如下:

start 方法调用了 start0 方法,start0 方法在 JVM 中,start0 中的逻辑会调用 run 方法。

至此,我们已经分析清楚从线程创建到 run 方法被执行的逻辑。但是通过实现 Runnbale 的方式实现多线程时,Runnable 的 run 方法是如何被调用的呢?

4.3 Runnable.run()方法源码分析

我们先从 Thread 的构造函数入手。原因是 Runnable 的实现对象通过构造函数传入 Thread。

Thread类构造方法源码:

public Thread(Runnable target) {    init(null, target, "Thread-" + nextThreadNum(), 0);
}

可以看到 Runnable 实现作为 target 对象传递进来。再次调用了 init 方法,init 方法有多个重载,最终调用的是Thread类中的如下方法:

private void init(ThreadGroup g, Runnable target, String name, long stackSize) {Thread parent = currentThread();if (g == null) {g = parent.getThreadGroup();}g.addUnstarted();this.group = g;this.target = target;this.priority = parent.getPriority();this.daemon = parent.isDaemon();setName(name);init2(parent);/* Stash the specified stack size in case the VM cares */this.stackSize = stackSize;tid = nextThreadID();
}

此方法里有一行代码:

this.target = target;

 原来 target 是 Thread类的成员变量:

/* What will be run. */
private Runnable target;

此时,Thread 的 target 被设置为你实现业务逻辑的 Runnable 实现。

我们再看下Thread类的run 方法的代码:

@Override
public void run() {if (target != null) {        target.run();    }
}

看到这里是不是已经很清楚了,当你传入了 target时(target不为null),在执行Thread类的run()方法时其实会调用执行 target 的 run 方法。也就是执行你实现业务逻辑的方法,我们需要在实现Runnable接口的类中实现Runnable接口的run()方法。整体执行流程如下:

如果你是通过继承 Thread,重写 run 方法的方式实现多线程。那么在上图中的第三步执行的就是你重写的 run 方法。

我们回过头看看 Thread 类的定义:

public class Thread implements Runnable

原来 Thread 也实现了 Runnable 接口。怪不得 Thread 类的 run 方法上有 @Override 注解。所以继承 Thread类实现多线程,其实也相当于是实现 Runnable 接口的 run 方法。只不过此时,不需要再传入一个 Thread 类去启动。它自己已具备了 Thread 的功能,自己就可以运转起来。既然 Thread 类也实现了 Runnable 接口,那么 Thread 子类对象是不是也可以传入另外的 Thread 对象,让其执行自己的 run 方法呢?答案是可行的。

4.4 总结

以上对多线程的两种实现方式做了分析。在学习多线程的同时,我们也应该学习源代码中优秀的设计模式。Java 中多线程的实现采用了模板模式Thread 是模板对象,负责线程相关的逻辑,比如线程的创建、运行以及各种操作。而线程真正的业务逻辑则被剥离出来,交由 Runnable 的实现类去实现。线程操作和业务逻辑完全解耦,普通开发者只需要聚焦在业务逻辑实现

执行业务逻辑,是 Thread 对象的生命周期中的重要一环。这一步通过调用传入 Runnable 的 run 方法实现。Thread 线程整体逻辑就是一个模板,把其中一个步骤剥离出来由其他类实现,这就是模板模式。


相关文章:【并发基础】线程,进程,协程的详细解释
                  【操作系统】一篇文章带你快速搞懂用户态和内核态

相关文章:

【并发基础】Java中线程的创建和运行以及相关源码分析

目录 一、线程的创建和运行 1.1 创建和运行线程的三种方法 1.2 三者之间的继承关系 二、Thread类和Runnable接口的区别 2.1 Runnable接口可以实现线程之间资源共享,而Thread类不能 2.2 实现Runnable接口相对于继承Thread类的优点 三、实现 Runnable 接口和实现 Call…...

Spark Shuffle

Shuffle : 集群范围内跨节点、跨进程的数据分发 分布式数据集在集群内的分发,会引入大量的磁盘 I/O 与网络I/O在 DAG 的计算中,Shuffle 环节的执行性能是最差的 , 会消耗所有类型的硬件资源 (CPU、内存、磁盘、网络) Spark 2.0 后,将 Shuff…...

Linux/MacOS 生成双击可执行文件

双击可执行文件包含两种:终端shell脚本 Unix可执行文件 1.终端shell脚本 随意新建一个文件(可使用command键N,前提是有已打开的文件),输入shell格式的测试代码,比如: #! /bin/sh echo “h…...

Ubuntu三种拨号方法

1.宽带拨号(PPPoE) (1)打开连接。关闭以太网连接,打开有线连接设置,取消勾选“自动连接”选项。 (2)配置连接。在终端输入命令sudo pppoeconf,会看到一系列配置信息,包括用户名、密码,配置完成后会有一些提示信息&…...

Vue-router的引入和安装

什么是Vue-Router?Vue路由器是Vue.js的官方路由器,它与Vue.js核心深度集成,使用Vue轻松构建单页应用程序变得轻而易举。功能包括:嵌套路线映射动态路由模块化,基于组件的路由器配置路由参数,查询&#xff0…...

无线WiFi安全渗透与攻防(四)之kismet的使用

系列文章 无线WiFi安全渗透与攻防(一)之无线安全环境搭建 无线WiFi安全渗透与攻防(二)之打造专属字典 无线WiFi安全渗透与攻防(三)之Windows扫描wifi和破解WiFi密码 kismet 如果要进行无线网络渗透测试,则必须先扫描所有有效的无线接入点。刚好在Kali Linux中&am…...

2023新版PMP考试有哪些变化?

对于2022年很多事情也都在发生,疫情也都没有完全结束,基金会已经开始通知下一场考试了,很多人也会担心新的考纲会不会给自己带来难度,其实这次六月份的考试很多人都内心已经知道了结果,所以这里也详细说一下新考纲的改…...

P8074 [COCI2009-2010#7] SVEMIR 最小生成树

[COCI2009-2010#7] SVEMIR 题目描述 太空帝国要通过建造隧道来联通它的 NNN 个星球。 每个星球用三维坐标 (xi,yi,zi)(x_i,y_i,z_i)(xi​,yi​,zi​) 来表示,而在两个星球 A,BA,BA,B 之间建造隧道的价格为 min⁡{∣xA−xB∣,∣yA−yB∣,∣zA−zB∣}\min\{|x_A-x_…...

10种常见网站安全攻击手段及防御方法

在某种程度上,互联网上的每个网站都容易遭受安全攻击。从人为失误到网络罪犯团伙发起的复杂攻击均在威胁范围之内。 网络攻击者最主要的动机是求财。无论你运营的是电子商务项目还是简单的小型商业网站,潜在攻击的风险就在那里。 知己知彼百战不殆&…...

为什么我选择收费的AdsPower指纹浏览器?

在决定开始用指纹浏览器之前,龙哥让我们团队的运营小哥找了市面上很多产品去测试。最后,还是决定用AdsPower。每个人的使用感受都不一样,龙哥就说说几个用得顺手的几个点。一、指纹环境强大 双内核引擎 市面上指纹浏览器内核都是基于谷歌Chro…...

Java输入输出和数组

一、问答题 1. 如何声明和创建一个一维数组&#xff1f; Int i[]new int[3] 2. 如何访问数组的元素&#xff1f; Int a[]new int a[3] for (int x0;x<a.length;x){ System.out.print(i[x]); } System.out.println(); 3.数组下标的类型是什么&#xff1f;最小的下标是什…...

这些免费API帮你快速开发,工作效率杠杠滴

一、短信发送 短信的应用可以说是非常的广泛了&#xff0c;短信API也是当下非常热门的API~ 短信验证码&#xff1a;可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商&#xff0c;3秒可达&#xff0c;99.99&#xff05;到达率&#xff0c;支持大容量高并发。…...

干货|最全PCB布线教程总结,14条PCB布线原则技巧,保姆级搞定PCB布线

1、坚持手动布线&#xff0c;慎用自动布线2、了解制造商的规格3、合适的走线宽度4、迹线之间留出足够的空间5、元器件放置6、保持模拟和数字走线分开7、接地层8、走线和安装孔留有足够的空间9、交替走线方向10、避免电容耦合11、放置散热孔和焊盘12、接地和电源走线13、利用丝印…...

编程快捷键和markdown语法小计

Data Structure FQA文章目录1.idea快捷键汇总2.markdown一些常用语法1.idea快捷键汇总 altenter  快捷生成变量 altInsert可以新建类&#xff0c;文件&#xff0c;get或set方法&#xff0c;此快捷键又名创造一切 编辑区和文件区的跳转。 alt 1  &#xff1a;编辑区跳转至…...

内网vCenter部署教程二,最全的了!

一、组网说明 vCenter组网最佳实践 每台服务器需要6个网口,需要三个分布式交换机,每个交换机分配2个物理网卡做冗余,分别做为管理网络、业务网络、高可用网络使用。另vsan网络和vmotion网络可以复用业务网络或管理网络,vcenter HA需要单独用一个网络。 二、创建管理网络…...

2023-3-2 刷题情况

迷宫 题目描述 这天, 小明在玩迷宫游戏。 迷宫为一个 nn 的网格图, 小明可以在格子中移动, 左上角为 (1,1), 右 下角 (n,n) 为终点。迷宫中除了可以向上下左右四个方向移动一格以外, 还有 m 个双向传送门可以使用, 传送门可以连接两个任意格子。 假如小明处在格子 (x1,y1)(…...

Docker SYS_ADMIN 权限容器逃逸

1.漏洞原理Docker容器不同于虚拟机&#xff0c;它共享宿主机操作系统内核。宿主机和容器之间通过内核命名空间&#xff08;namespaces&#xff09;、内核Capabilities、CGroups&#xff08;control groups&#xff09;等技术进行隔离。若启动docker容器时给主机一个--cap-addSY…...

【Kotlin】 yyyy-MM-dd HH:mm:ss 时间格式 时间戳 全面解读超详细

时间格式 时间格式(协议)描述gg时期或纪元。y不包含纪元的年份。不具有前导零。yy不包含纪元的年份。具有前导零。yyyy包含纪元的四位数的年份。M月份数字。一位数的月份没有前导零。MM月份数字。一位数的月份有一个前导零。MMM月份的缩写名称&#xff0c;在AbbreviatedMonthN…...

git repack多包使用及相关性能测试

1、git数据结构 git 中存在四种数据结构&#xff0c;即object包含四种&#xff0c;分别是tree对象、blob对象、commit对象、tag对象 1.1 blob对象 存储文件内容&#xff0c;内容是二进制的形式&#xff0c;通过SHA-1算法对文件内容和头信息进行计算得到key(文件名)。 如果一…...

QT获取dll库文件详细信息

一、需求背景获取软件下依赖的dll库的版本信息&#xff0c;如下图所示版本为1.0.7.1018二、实现方法2.1步骤windows下实现&#xff0c;基于version.lib(version.dll)提供的函数获取这些信息首先使用GetFileVersionInfoSizeA(W)获取VersionInfo的大小&#xff0c;申请缓冲区&…...

常见的电脑运行卡顿原因及解决方法

大家在日常使用电脑过程中&#xff0c;会发现多开几个文件就卡顿&#xff0c;其实很多时候都跟C盘长期不清理有关&#xff0c;C盘的内存被下载的软件安装包、页面文件、休眠文件、更新文件等一系列的文件占据。大的文件甚至能占到20-30G&#xff0c;驱动人生就为大家带来几种解…...

案例08-让软件的使用者成为软件的设计者

一&#xff1a;背景介绍 对于需求的开发每天可能都会有上线的情况&#xff0c;为了防止每次上线拉取代码或者修改配置而引发的冲突以及发生了冲突应该找谁一起确定一下代码留下那一部分的情况。所以在开发的群中会有一个表格来记录每个需求上线修改的环境、是否修改数据库、是否…...

QinQ与Vlan Mapping讲解

目录 QinQ Vlan扩展 QinQ实现方式 QinQ实验配置 Vlan Mapping Vlan映射 映射方式 配置命令 QinQ Vlan扩展 QinQ全称为802.1Q-in-802.1Q&#xff0c;为Vlan扩展技术&#xff0c;在802.1Q标签报文的基础上再增加一层802.1Q标签&#xff0c;实现扩展Vlan空间&#xff1b;可…...

golang 获取token方法

package main import ( "fmt" "time" "github.com/dgrijalva/jwt-go" ) const ( SECRETKEY "202203021124355xxx" //私钥 ) // 自定义 Claims type CustomClaims struct { UserId int64 jwt.StandardClaims } func main() { //生…...

【数据库专题】数据库Mongodb之深入认知云计算三种服务方式、mongodb特点、mongodb重要进程 mongod、mongo、其他进程区别

文章目录一、什么是云计算1. IaaS:基础设施即服务2. SaaS:软件即服务3. PaaS:平台即服务二、大数据与云计算关系三、什么是MongoDB四、大数据与MongoDB五、MongoDB特点六、安装MongoDB七、重要进程介绍7.1 mongod进程7.2 mongo进程7.3 其他进程7.3.1 mongodump重建数据库7.3.2 …...

ccc-pytorch-小实验合集(4)

文章目录一、 Himmelblau 优化二、多分类实战-Mnist三、Sequential与CPU加速-Mnist四、visidom可视化一、 Himmelblau 优化 Himmelblau 是一个具有4个最优值的2维目标函数。其函数和最优值点如下&#xff1a; 图象绘制&#xff1a; import numpy as np from matplotlib impo…...

webrtc音频系列——4、RTP与RTCP协议

如果让你从0开发一套实时互动直播系统&#xff0c;你首先要选择网络传输协议。UDP 还是 TCP&#xff1f;答案是&#xff1a;UDP。为什么实时传输不能用 TCP &#xff1f;TCP 的目的就是实现数据的可靠传输&#xff0c;因此他有一套 握手&#xff0c;发送 -> 确认&#xff0c…...

C++枚举解读(enum)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、枚举是什么&#xff1f;二、使用步骤1.作用域2.隐式类型转换3.显式指定枚举值类型4.指定枚举值的值4.整形显式转换成枚举总结前言 对于开发C来说&#xff0…...

OSCP-课外5(Web图片泄露服务信息、日志中毒)

目录 一、主机发现与端口扫描 二、Web信息收集 三、系统信息收集与提权 一、主机发现与端口扫描...

汇编指令学习(ADD,SUB,MUL,DIV,XADD,INC,DEC,NEG)

一、ADD加法操作指令将eax置1&#xff0c;ebx置2&#xff0c;运行下面命令&#xff0c;将结果保存到eaxadd eax,ebx扩展&#xff1a;adc需要再加上CF标志位的值adc eax&#xff0c;ebx二、SUB减法操作指令将eax置3&#xff0c;ebx置2&#xff0c;运行下面命令&#xff0c;将结果…...

网站推广明细报价表/搜索引擎平台

作为一名全职的Go语言作家和老师&#xff0c;我花了很多时间和学生们一起&#xff0c;帮助他们写出更清晰、更好、更有用的Go程序。我发现&#xff0c;我给他们的建议可以归纳总结为一套通用原则&#xff0c;在这里我将这些原则分享给大家。1、你应该是无聊的Go社区喜欢共识&am…...

外贸网站建设如何做呢/网络软文范文

String.substring( )&#xff1a;用于返回一个字符串的子串 用法如下&#xff1a;string.substring(from, to) 其中from指代要抽去的子串第一个字符在原字符串中的位置 to指代所要抽去的子字符串最后一个字符的后一位&#xff08;这个参数可以不加&#xff09; 下面就对String…...

asp网站制作教程/willfast优化工具下载

来到这边,开始工作后,才发现.有的时候,心态变了.一切都会改变.以前总是把自己会的技术挂在嘴边.而这次,更多的是踏踏实实的做出来.没有什么可骄傲的.一切都没有. 中午在机房里呆了一中午,又一次熟悉了设备及常用命令.在听完郭老师的培训后,才发现,自己是那么的无知.知识的深度及…...

网站后台密码忘记了怎么办/如何建立自己的网站平台

二维数组的定义 二维数组的应用 定义一个数组&#xff0c;存储五名学生的三门成绩求出每名学生的总成绩 、平均成绩求出每门学科的总成绩&#xff0c;平均成绩 C语言解法 #define _CRT_SECURE_NO_WARNINGS #define ROW 2 #define COL 3 #include<stdio.h>// 学生的平均…...

如何知道自己网站租用的服务器去/seo运营学校

1.1 今日目标理解多表查询理解子查询能够创建视图能够删除视图能够查看创建视图的SQL语句能够理解事务的作用能够操作事务理解索引的作用能够创建索引能够删除索引知道常用的函数了解预处理语句的作用能够使用预处理语句了解存储过程的作用能够创建存储过程能够调用存储过程能够…...

扬中市做网站/免费推广产品的网站

忽然之间&#xff0c;美国食品药物管理局&#xff08;以下简称FDA&#xff09;成了AI医疗亲密无间的好朋友和坚定的支持者。 2018年上半年&#xff0c;FDA相继批准癫痫监测与警报AI手表Embrace、AI临床监测平台Wave、脑卒中护理软件Viz.ai、自闭症筛查AI软件Cognoa以及血糖预测…...