java如何阻塞和同步_同步与异步,阻塞与非阻塞

今天早上关注了这个问题,刚抽出时间大概整理下,以下仅是个人理解:

一定要多看几遍代码并结合文字理解下

引0、从I/O说起

这些概念之所以容易令人迷惑,在于很多人对I/O就没有清晰准确的理解,后面的理解自然不可能正确。我想用一个具体的例子来说明一下I/O。

设想自己是一个进程,就叫小进吧。小进需要接收一个输入,我们不管这个输入是从网络套接字来,还是键盘,鼠标来,输入的来源可以千千万万。但是,都必须由内核来帮小进完成,为啥内核这么霸道?因为计算机上运行的可不只是咱小进一个进程,还有很多进程。这些进程兄弟也可能需要从这些输入设备接收输入,没有内核居中协调,岂不是乱套。

从小进的角度看,内核帮助它完成输入,其实包括三个步骤:内核替小进接收好数据,这些数据暂时存在内核的内存空间

内核将数据从自己的内存空间复制到小进的内存空间

告诉小进,输入数据来了,赶快读吧

这三步看似挺简单,其实在具体实现时,有很多地方需要考虑:小进如何告诉内核自己要接收一个输入?

内核接到小进的请求,替小进接收好数据这段时间, 小进咋办?

内核在将数据复制到小进的内存空间这段时间,小进咋办?

到底什么时候告诉小进数据准备好了,是在内核接收好数据之后就告诉小进,还是在将数据复制到小进的内存空间之后再告诉他?

内核以什么样的方式告诉小进,数据准备好了?

1、阻塞式I/O模型

对上面5个问题,最简单的解决方案就是阻塞式I/O模型,它的过程是这样的:

小进:内核内核,我要接收一个键盘输入,快点帮我完成!

内核:好咧!biubiu!一个阻塞丢给小进,小进顿时石化,就像被孙悟空点了定一样。

就这样,小进在石化中,时间一点点流逝。终于,内核收到了数据。

内核:数据终于来了,我要开干了!duang duang duang,先把数据存在自己的内核空间,然后又复制到小进的用户空间。

内核:biubiu!一个解除阻塞丢给小进,小进瞬间复活,小进的记忆还是停留在让内核帮他接收输入时。

小进:哇!内核真靠谱,数据已经有了!干活去!

我们可以看到,小进发出接收输入的请求给内核开始,就处于阻塞状态,直到内核将数据复制到小进的用户空间,小进才解除阻塞。

2、非阻塞式I/O

小进发现,阻塞式I/O中,自己总要被阻塞好久,好不爽啊,于是小进改用了非阻塞式I/O,其过程是这样的:

小进:内核内核,我要接收一个输入,赶紧帮我看看,数据到了没有,先说好,不要阻塞我。

内核:查看了一下自己的内核空间,没有发现数据,于是迅速告诉小进,没有呢!并继续帮小进等着数据。

如此这样,小进不断地问内核,终于,过了一段时间,小进再一次询问时,内核往自己的空间中一查,呦!数据来了,不胜其烦的内核迅速告诉小进,数据好了!

小进:快给我!

内核:biu!一个阻塞丢给小进,悲催的小进还是石化了!

内核赶紧将自己空间的输入数据复制到小进的用户空间,复制好后。

内核:biu!一个非阻塞丢给小进,小进立马复活

小进:哇!数据来了,啥也不说,干活!

我们看到,所谓的非阻塞I/O,其实在内核将数据从内核空间复制到小进的用户空间时,小进还是被阻塞的。

3、信号驱动式I/O

非阻塞I/O中,小进不停地问内核,数据好了没有啊,内核感觉太烦了,于是想出一个好办法。

内核告诉小进,本内核升级了,如果想要我替你接收输入,请先注册一个信号处理函数,等数据准备好时,我会发信号给你。于是,现在的流程是这样的:

小进:注册信号处理函数,告诉内核,自己要接收一个输入,然后继续干活!

内核:收到函数,开始执行数据接收

接收完成时,给小进发送信号,信号处理函数收到信号,开始向内核发送读数据请求

内核:biu!阻塞了小进,并把数据从内核空间复制到小进的用户空间。

内核:biu!解除了阻塞

小进:哇!数据来了!啥也不说,干活去!

4、异步I/O

上面的三种I/O解决方案中,小进都被阻塞了,只不过是阻塞时间长短不一样,第一种方案中小进被阻塞的时间长一些,在内核接收数据以及将数据复制到小进的用户空间时,都被阻塞。

第二、第三种方案中,只在内核将数据从内核空间复制到小进的用户空间时,小进才被阻塞。

我们现在说的异步I/O,目的就是让小进绝对不被阻塞。其过程是这样的:

小进:内核内核,我要接收一个输入,弄好了告诉我。同时将一个信号和信号处理函数告诉内核,然后继续干自己的活了。

内核:得了您嘞,您先忙。

一直到内核接收到数据并将数据从内核空间复制到小进的用户空间后,内核才给小进发送信号。小进在信号处理函数中可以直接处理数据。

1、阻塞式I/O式

客户端代码public class Client {

public static void main(String[] args) {

Socket socket = null;

try {

System.out.println("socket begin " + System.currentTimeMillis());

// 随机绑定本地地址与端口

socket = new Socket("localhost", 8888);

System.out.println("socket end " + System.currentTimeMillis());

OutputStream os = socket.getOutputStream();

Random ran = new Random();

for (int n = 0; n < 10; n++) {

System.out.println("send message " + n);

os.write(("hello server form " + socket.getLocalAddress().getHostAddress() + " - " + n).getBytes());

try {

TimeUnit.SECONDS.sleep(ran.nextInt(10));

} catch (InterruptedException e) {

break;

}

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (socket != null) {

// 自动关闭绑定流

socket.close();

}

System.out.println("exit");

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

服务端代码public class Server {

public static void main(String[] args) {

ServerSocket serverSocket = null;

Socket socket = null;

ThreadPoolExecutor executor = null;

try {

// 初始化线程池

executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),

Runtime.getRuntime().availableProcessors() * 2,

0L, TimeUnit.SECONDS, new LinkedBlockingDeque(), new ThreadBuilder());

// 监听通配符地址

serverSocket = new ServerSocket(8888);

System.out.println("accept begin " + System.currentTimeMillis());

while ((socket = serverSocket.accept()) != null) {

executor.execute(new Task(socket));

}

System.out.println("accept end " + System.currentTimeMillis());

} catch (IOException e) {

e.printStackTrace();

} finally {

executor.shutdown();

try {

if (serverSocket != null) {

serverSocket.close();

}

System.out.println("exit");

} catch (IOException e) {

e.printStackTrace();

}

}

}

static class ThreadBuilder implements ThreadFactory {

private AtomicInteger counter = new AtomicInteger(0);

@Override

public Thread newThread(Runnable r) {

Thread thread = new Thread(r, "server-thread-" + counter.getAndIncrement());

thread.setUncaughtExceptionHandler((t, e) -> {

if (e instanceof TaskException) {

System.err.println(t.getName() + "|" + e.getCause().getMessage());

} else {

System.err.println(t.getName() + "|" + e.getMessage());

}

});

return thread;

}

}

static class Task implements Runnable {

private byte[] buffer = new byte[10 * 1024];

private Socket socket;

public Task(Socket socket) {

this.socket = socket;

}

@Override

public void run() {

try {

InputStream is = socket.getInputStream();

System.out.println("--------------------------------------------------");

System.out.println("read begin " + System.currentTimeMillis());

System.out.println("***");

int len = is.read(buffer);// 呈阻塞效果

while (len != -1) {

String str = new String(buffer, 0, len);

System.out.println(str);

len = is.read(buffer);

}

System.out.println("***");

System.out.println("read end " + System.currentTimeMillis());

System.out.println("--------------------------------------------------");

} catch (IOException e) {

throw new TaskException(e);

} finally {

if (socket != null) {

try {

// 自动关闭绑定流

socket.close();

System.out.println("socket closed");

} catch (IOException e) {

System.err.println("socket close failed");

}

}

}

}

}

static class TaskException extends RuntimeException {

public TaskException(Throwable cause) {

super(cause);

}

}

}

2、非阻塞式I/O

客户端代码同上

服务端代码public class Server {

public static void main(String[] args) {

ServerSocketChannel serverSocket = null;

SocketChannel socket = null;

ThreadPoolExecutor executor = null;

try {

// 初始化线程池

executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),

Runtime.getRuntime().availableProcessors() * 2,

0L, TimeUnit.SECONDS, new LinkedBlockingDeque(), new ThreadBuilder());

serverSocket = ServerSocketChannel.open();

// 设置阻塞

serverSocket.configureBlocking(true);

// 监听通配符地址

serverSocket.bind(new InetSocketAddress(8888));

System.out.println("accept begin " + System.currentTimeMillis());

while ((socket = serverSocket.accept()) != null) {

// 设置非阻塞

socket.configureBlocking(false);

executor.execute(new Task(socket));

}

System.out.println("accept end " + System.currentTimeMillis());

} catch (IOException e) {

e.printStackTrace();

} finally {

executor.shutdown();

try {

if (serverSocket != null) {

serverSocket.close();

}

System.out.println("exit");

} catch (IOException e) {

e.printStackTrace();

}

}

}

static class ThreadBuilder implements ThreadFactory {

private AtomicInteger counter = new AtomicInteger(0);

@Override

public Thread newThread(Runnable r) {

Thread thread = new Thread(r, "server-thread-" + counter.getAndIncrement());

thread.setUncaughtExceptionHandler((t, e) -> {

if (e instanceof TaskException) {

System.err.println(t.getName() + "|" + e.getCause().getMessage());

} else {

System.err.println(t.getName() + "|" + e.getMessage());

}

});

return thread;

}

}

static class Task implements Runnable {

private ByteBuffer buffer = ByteBuffer.allocate(10 * 1024);

private SocketChannel socket;

public Task(SocketChannel socket) {

this.socket = socket;

}

@Override

public void run() {

try {

System.out.println("--------------------------------------------------");

System.out.println("read begin " + System.currentTimeMillis());

System.out.println("***");

socket.read(buffer);// 呈阻塞效果

while (true) {

if (buffer.position() == 0) {

try {

TimeUnit.MILLISECONDS.sleep(200);

} catch (InterruptedException e) {

continue;

}

} else {

buffer.flip();

String str = new String(buffer.array(), 0, buffer.limit());

System.out.println(str);

if ("exit".equals(str)) {

break;

}

buffer.clear();

}

socket.read(buffer);

}

System.out.println("***");

System.out.println("read end " + System.currentTimeMillis());

System.out.println("--------------------------------------------------");

} catch (IOException e) {

throw new TaskException(e);

} finally {

if (socket != null) {

try {

// 自动关闭绑定流

socket.close();

System.out.println("socket closed");

} catch (IOException e) {

System.err.println("socket close failed");

}

}

}

}

}

static class TaskException extends RuntimeException {

public TaskException(Throwable cause) {

super(cause);

}

}

}

3、多路复用式I/O(基于非阻塞式I/O)

客户端代码同上

服务端代码public class Server {

public static void main(String[] args) {

Selector selector = null;

ServerSocketChannel serverSocket = null;

SocketChannel socket = null;

ThreadPoolExecutor executor = null;

try {

// 初始化线程池

executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),

Runtime.getRuntime().availableProcessors() * 2,

0L, TimeUnit.SECONDS, new LinkedBlockingDeque(), new ThreadBuilder());

selector = Selector.open();

serverSocket = ServerSocketChannel.open();

// 设置非阻塞

serverSocket.configureBlocking(false);

// 监听通配符地址

serverSocket.bind(new InetSocketAddress(8888));

serverSocket.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("accept begin " + System.currentTimeMillis());

while (true) {

selector.select();

Iterator iterator = selector.selectedKeys().iterator();

while (iterator.hasNext()) {

SelectionKey key = iterator.next();

if (key.isAcceptable()) {

ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

SocketChannel socketChannel = serverSocketChannel.accept();

// 设置非阻塞

socketChannel.configureBlocking(false);

socketChannel.register(selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {

SocketChannel socketChannel = (SocketChannel) key.channel();

executor.execute(new Task(socketChannel));

key.cancel();

} else {

// TODO 写事件注册

}

iterator.remove();

}

}

// System.out.println("accept end " + System.currentTimeMillis());

} catch (IOException e) {

e.printStackTrace();

} finally {

executor.shutdown();

try {

if (serverSocket != null) {

serverSocket.close();

}

System.out.println("exit");

} catch (IOException e) {

e.printStackTrace();

}

}

}

static class ThreadBuilder implements ThreadFactory {

private AtomicInteger counter = new AtomicInteger(0);

@Override

public Thread newThread(Runnable r) {

Thread thread = new Thread(r, "server-thread-" + counter.getAndIncrement());

thread.setUncaughtExceptionHandler((t, e) -> {

if (e instanceof TaskException) {

System.err.println(t.getName() + "|" + e.getCause().getMessage());

} else {

System.err.println(t.getName() + "|" + e.getMessage());

}

});

return thread;

}

}

static class Task implements Runnable {

private ByteBuffer buffer = ByteBuffer.allocate(10 * 1024);

private SocketChannel socket;

public Task(SocketChannel socket) {

this.socket = socket;

}

@Override

public void run() {

try {

System.out.println("--------------------------------------------------");

System.out.println("read begin " + System.currentTimeMillis());

System.out.println("***");

socket.read(buffer);// 呈阻塞效果

while (true) {

if (buffer.position() == 0) {

try {

TimeUnit.MILLISECONDS.sleep(200);

} catch (InterruptedException e) {

continue;

}

} else {

buffer.flip();

String str = new String(buffer.array(), 0, buffer.limit());

System.out.println(str);

if ("exit".equals(str)) {

break;

}

buffer.clear();

}

socket.read(buffer);

}

System.out.println("***");

System.out.println("read end " + System.currentTimeMillis());

System.out.println("--------------------------------------------------");

} catch (IOException e) {

throw new TaskException(e);

} finally {

if (socket != null) {

try {

// 自动关闭绑定流

socket.close();

System.out.println("socket closed");

} catch (IOException e) {

System.err.println("socket close failed");

}

}

}

}

}

static class TaskException extends RuntimeException {

public TaskException(Throwable cause) {

super(cause);

}

}

}

4、信号驱动式I/O

JAVA没有实现

5、异步I/O

客户端代码同上

服务端代码public class Server {

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {

AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();

serverSocketChannel.bind(new InetSocketAddress(8888));

serverSocketChannel.accept(null, new CompletionHandler() {

public void completed(AsynchronousSocketChannel asc, Void att) {

serverSocketChannel.accept(null, this);

ByteBuffer byteBuffer = ByteBuffer.allocate(10 * 1024);

asc.read(byteBuffer, null, new CompletionHandler() {

@Override

public void completed(Integer result, Void attachment) {

byteBuffer.flip();

System.out.println(new String(byteBuffer.array(), 0, byteBuffer.limit()));

byteBuffer.clear();

try {

asc.close();

} catch (IOException e) {

}

}

@Override

public void failed(Throwable exc, Void attachment) {

}

});

}

public void failed(Throwable exc, Void att) {

}

});

for (; ; ) {

}

}

}

牛奶工送牛奶场景阻塞式:每天早上自己去小区门口等牛奶工

非阻塞式:每天早上在家从窗户早上隔3分钟看看牛奶工到了没,到了的话去拿

多路复用式:每天早上由小区门卫室接待所有牛奶工,到了会给住户发短信,你马上去拿

信号驱动式:每天早上牛奶工到了会给你发短信,你马上去拿

异步式:每天早上牛奶工直接放到小区住户牛奶柜并发短信,不需要现在去拿

程序分为CPU计算型和I/O读写型,线程尤其是被内核调度的线程是及其珍贵的资源(JAVA计划在JDK将来的版本实现由JVM”自己“调度的轻型线程),在有限的线程资源下CPU计算型程序不但不会有明显提升,反而由于频繁的上下文切换导致性能下降(这也是Redis这种基于内存的数据库采用单工作线程并且速度非常快的原因,另一个重要的原因是单线程导致了不用为共享资源给线程加/解锁造成人为阻塞),而在I/O读写型的程序中,多线程工作在以上五种模式下性能是逐步提升的(最后多说一句,还是以Redis举例,不管是Jedis-Pool这种池化客户端还是Lettuce这种单连接客户端,当多用户接入Redis服务器时一定是多连接的,这时候就要用到多路复用来处理用户请求了,至于为什么没有用异步,一个原因是工作线程是单线程,另一个原因是异步I/O模型在性能提升方面有限并且复杂度高,以至于Netty在新版本的包中把这种模式删除了)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/562925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中文乱码破碎重组_总结彻底解决Spring MVC+Mybatis中文乱码问题

Java对于新手最容易出现的问题就是中文乱码的问题。今天我就来总结一下彻底解决Spring mvcMybatis中文乱码的方案。首先要看打一断点看一下Controller接收到参数值是否正常。如果不正常多半是因为Spring或者页面编码的设置问题。一、Spring或页面编码问题在JSP页面第一行加上下…

编写Java程序 堆栈的接口_java - 错误:调用实现接口的通用方法时,java.lang.AbstractMethodError - 堆栈内存溢出...

我正在尝试使用javassist以编程方式创建和编译实现接口的类(在运行时)。每当我调用该动态类的实例时&#xff0c;都会收到以下错误消息&#xff1a;java.lang.AbstractMethodError: FooImpl.test()Ljava/lang/Object;这是我的界面public class FooBarInterface {public T getEn…

c java开发对比_编程语言:Java和C语言的比较,你更偏向于哪个?老程序员:选C...

现在在编程语言排行中&#xff0c;Java毫无争议的排在第一位&#xff0c;Python以及C语言紧随其后。现在很多人拿Java以及C语言作比较&#xff1a;有人认为Java作为当前最热门的编程语言&#xff0c;学习Java的出路最大或者说作为一个合格的程序员一定要了解学习一下Java编程&a…

Java8的产品周期_java8的时间和`Date`的对比

java8提供了新的时间接口。相对Date,Calendar,个人感觉最大的好处是对时间操作的学习成本很低&#xff0c;比Calendar低。1. LocalDate,LocalTime,LocalDateTimeLocalDate 代表日期&#xff0c;LocalTime表示时刻&#xff0c;类似11:23这样的时刻。 LocalDateTime就是前面2个的…

java怎么设置多个输入_Java中从键盘输入多个整数的方法

Java中从键盘输入多个整数的方法发布于 2020-4-2|复制链接分享一篇关于Java中从键盘输入多个整数的方法&#xff0c;具有很好的参考价值&#xff0c;希望对大家有所帮助。一起跟随小妖过来看看吧例题&#xff1a;求数列的和 分别输入两个整数n&#xff0c;m,中间以空格隔断&…

从java代码获取类名_java代码获取当前类类名、方法名

获取类名&#xff1a;1、在类的实例中可使用this.getClass().getName();但在staticmethod中不能使用该方法&#xff1b;2、在staticmethod中使用方法:Thread.currentThread().getStackTrace()[1].getClassName();获取方法名&#xff1a;Thread.currentThread().getStackTrace()…

linux将mysql导出表数据导入另一台服务器_远程linux服务器mysql数据库导入和导出.sql文件...

今天对需要将服务器上的数据库放到本地环境&#xff0c;之前没怎么这么操作过&#xff0c;ORZ&#xff0c;弱鸡&#xff0c;这篇帖子不错&#xff0c;收藏了&#xff01;&#xff01;&#xff01;远程linux服务器mysql数据库导入和导出.sql文件大部分情况本地开发环境为windows…

java cpu高_Java中的CPU占用高和内存占用高的问题排查

下面通过模拟实例分析排查Java应用程序CPU和内存占用过高的过程。如果是Java面试&#xff0c;这2个问题在面试过程中出现的概率很高&#xff0c;所以我打算在这里好好总结一下。1、Java CPU过高的问题排查举个例子&#xff0c;如下&#xff1a;package com.classloading;public…

solr 时间转成java_solr时区设置解决时间多8小时问题

今天发现我博客里文章的时间与实际时间差了8小时&#xff0c;正好比原来时间多出8小时&#xff0c;想想应该是时区不一致的问题。用solr web工具查询出来的数据原样&#xff1a;"create_time": "2018-01-18T11:22:06Z"{"original": 1,"crea…

php if 单行,php – 单行if语句或if语句没有大括号坏习惯?

最好的做法是编写别人可以轻松读取和更新的代码。您的第一个表单是有问题的&#xff0c;因为它不遵循大多数PHP开发人员使用的表单&#xff1a;if (condition) {// code} else {// code}// ... or ...if (condition){// code}else{// code}// ... or ...if (condition) { /* sh…

php 错误 异常,php中的异常和错误解析

PHP错误是属于php程序自身的问题&#xff0c;一般是由非法的语法&#xff0c;环境问题导致的&#xff0c;使得编译器无法通过检查甚至无法运行的情况。PHP异常一般是业务逻辑上出现的不合预期、与正常流程不同的状况&#xff0c;不是语法错误。本文介绍了php中异常和错误的相关…

php变量原格式输出,PHP格式化输出打印变量

PHP 常用的输入变量函数print_r&#xff0c;但是输出没有换行&#xff0c;看起来很费力 我们可以自定义一个函数来实现变量的格式化输出&#xff0c;代码如下&#xff1a; function dump($vars, $label , $return false) {if (ini_get(html_errors)) {$content "\n&quo…

一个控制器2套模版php,如何不用一个自定义的控制器渲染一个模板

如何不用一个自定义的控制器渲染一个模板通常&#xff0c;当您需要创建一个页面&#xff0c;您需要创建一个控制器并且从该控制器中呈现模板。但如果您仅仅呈现一个简单的模板&#xff0c;并且不需要传递给它的任何数据&#xff0c;则完全没必要创建一个控制器&#xff0c;通过…

JAVA语言写的,用java语言写

Caballariistatic int argument;static int count0;public static void main(String[] args){argument5;printResult("",argument);System.out.println("总共"count"种");}public static void printResult(String result,int left){if(left1){Sy…

accept标头 php,解决PHP中缺少“授权”请求标头的问题

我目前正在为一个uni项目开发PHP REST API,该项目使用通过PhoneGap从移动Web应用程序传递的JSON Web令牌,或者在开发过程中使用我的桌面.使用ajax将令牌发送到我的服务器页面“ friends / read.PHP”时,服务器使用以下命令正确获取了Authorization标头$headers getallheaders(…

handsome对应php文件,基于handsome主题的一些美化总结

用了handsome主题有一段时间了&#xff0c;挺不错的主题!在无数次更换各种博客程序后最终还是选择使用Typecho&Handsome主题搭建博客如果长久发展的话建议就用wordpress进入正题:代码没标明的都是添加至handsome主题后台主题设置 自定义CSS。首页头像转动并放大.thumb-lg{w…

php语录网站,杨泽业:给你的wordpress博客添加经典语录功能,适合所有php网站

我们做一个网站就是要不断给访客提供最佳的用户体验&#xff0c;同时提升自己的品牌价值&#xff0c;而文章结束的经典语录的功能&#xff0c;完全可以实现我们所要达到的效果。(如下图)给你的wordpress博客添加经典语录功能这个是怎样实现的呢&#xff1f;其实很简单&#xff…

matlab搭建的应用层架构逻辑,软件设计的三层架构

1&#xff1a;三层架构是什么&#xff1f;三层架构是一个分层式的软件体系架构设计理念。把软件架构分为三层&#xff1b;1&#xff1a;UI层 (user interface layer) 界面层2&#xff1a;BLL层 (business logic layer) 业务逻辑层3&#xff1a;DAL层 (data access laye…

mysql null 0 空,MySQL的空值和NULL区别

从本质上区别&#xff1a;1、空值不占空间2、null值占空间通俗的讲&#xff1a;空值就像是一个真空转态杯子&#xff0c;什么都没有&#xff0c;而null值就是一个装满空气的杯子&#xff0c;虽然看起来都是一样的&#xff0c;但是有着本质的区别。例子&#xff1a;创建一个test…

mysql 数据库中数据去重,oracle数据库中如何达到像mysql数据库中group by 那种去重的效果..求大神解答...

满意答案oracle和mysql&#xff0c;group by是由区别的。oracle的gruop by 后跟的必须是select查询出的字段而且group by语句中select指定的字段必须是“分组依据字段”&#xff0c;其他字段若想出现在select中则必须包含在聚合函数中聚合函数比如&#xff1a;sum(列名) 求和ma…