学习时间 0722
什么是BIO模式?
知识点参考 本文转发自技术世界,原文链接 http://www.jasongj.com/java/nio_reactor/
1、同步 vs. 异步
-
同步I/O 每个请求必须逐个地被处理,一个请求的处理会导致整个流程的暂时等待,这些事件无法并发地执行。
- 用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成后才能继续执行。
-
异步I/O 多个请求可以并发地执行,一个请求或者任务的执行不会导致整个流程的暂时等待。
- 用户线程发起I/O请求后仍然继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
2、阻塞 vs. 非阻塞
-
阻塞 某个请求发出后,由于该请求操作需要的条件不满足,请求操作一直阻塞,不会返回,直到条件满足。
-
非阻塞 请求发出后,若该请求需要的条件不满足,则立即返回一个标志信息告知条件不满足,而不会一直等待。一般需要通过循环判断请求条件是否满足来获取请求结果。
目前学习的JavaIO就是属于阻塞式IO 即 BIO
3、BIO模式
- Java IO的各种流是阻塞的。当某个线程调用read()或write()方法时,该线程被阻塞,直到有数据被读取到或者数据完全写入。阻塞期间该线程无法处理任何其它事情。
如上文所述,阻塞I/O下请求无法立即完成则保持阻塞。阻塞I/O分为如下两个阶段。
- 阶段1:等待数据就绪。网络 I/O 的情况就是等待远端数据陆续抵达;磁盘I/O的情况就是等待磁盘数据从磁盘上读取到内核态内存中。
- 阶段2:数据拷贝。出于系统安全,用户态的程序没有权限直接读取内核态内存,因此内核负责把内核态内存中的数据拷贝一份到用户态内存中。
线程
一、概念
- 操作系统:windows
- 操作系统下有很多进程
- 进程的切换:时间消耗较多 <————> 线程的切换 较快
- 进程:指正在进行运行的程序 (Process类)
- 应用程序 : 特指有界面的进程
- 平常在java中运行main方法,这就是一个进程
- 特点:
- 自己独立的内存空间
- 一个时间只能做一件事情
- 每个进程都会独立占用内存,并且里面会占用若干个线程
- 每个进程肯定有一个主线程
- 在资源管理器可以查看每个进程占用的线程
- 应用程序 : 特指有界面的进程
- 线程——>?
1、什么是线程(Thread类)
- 需要共享进程的内存 (简单理解:儿子需要用爸爸的钱
二、Thread类
-
如何获取主线程?
Thread thread = Thread.currentThread(); System.out.println("当前的线程是:"+thread.getName());
-
如何创建线程?
-
创建一个线程类,并且继承Thread类
-
重写 run()方法
里面的内容,就是 这个线程要做的事情:实现的功能!
-
创建一个该类的实例化对象
-
执行该对象
-
-
Thread类的run()和start()的区别
- run方法并没有开启新线程,实际情况:按照顺序执行,当对应的线程执行完毕后才会执行其他
- start方法才能真正开启新线程,让两个线程交叉运行
-
交叉运行和同时运行的区别
- CPU一次执行执行一个进程,但是
- 因为CPU的主频很高,比如
2.4GHz
=2.4 * 1024 * 1024 * 1024
次/秒- 表示一秒钟可以运行的次数
- CPU运行的速度很快,所以看起来像同时运行
-
举个例子
public class ThreadDemo extends Thread{ @Override public void run() { // super.run(); System.out.println("当前线程是"+Thread.currentThread().getName()); for (int i = 0; i < 10; i++) { System.out.println("线程的结果"+i); try { // 使用Thread.sleep()方法,可以让线程休眠指定的一段时间(毫秒) Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Test { public static void main(String[] args) { // 主线程 System.out.println("当前线程是"+Thread.currentThread().getName()); // 开启了一个线程 ——> 创建一个线程的实例化对象 ThreadDemo threadDemo = new ThreadDemo(); // 注意 :启动线程是启动start()方法 // threadDemo.start(); // 如果使用run() , 没有开启线程,会按照顺序执行,当run的线程执行完毕后才会执行其他线程 threadDemo.run(); // 让两个线程都执行同样的内容,可以发现两个线程都是同时运行的 for (int i = 0; i < 10; i++) { System.out.println("main方法的结果"+i); try { // 使用Thread.sleep()方法,可以让线程休眠指定的一段时间(毫秒) Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
三、线程结合BIO模式
1、一对多,接受多人信息
- 创建一个新的线程类,构造函数传入Socket对象
- 在线程类里,重写run方法内 :加入 **对Socket的对象的操作(**如获取信息,输出信息
- 在主方法内,先开启端口服务
- 在while循环内,只要建立新的连接,就实例化一个线程对象,并执行run方法
2、多对多,实现多人群聊
- 建立一个全局的静态变量
ArrayList<Socket>
- 在创建新的Socket对象时,将这个对象存进这个列表里
- 简单理解:每个连接的用户都会有一个Socket对象,存列表内后续可以遍历
- 在线程内,输出信息时,先遍历列表,再输出每个Socket对象内存放的信息
- 注意:在使用输出流,先判断这个Socket是否关闭
举个例子
public class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
while (true) {
// 获取 快速转换:原始的字节流——字符流——封装流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String lineStr = bufferedReader.readLine();
System.out.println("接收到的数据为:" + lineStr);
System.out.println("====实现群聊功能(转发其他用户的消息 ) 列表长度" + ServerChat.socketList.size());
// 遍历列表获取Socket对象
for (Socket otherSocket : ServerChat.socketList) {
// 如果没有关闭 在执行后续操作
if (!otherSocket.isClosed()) {
// 发送输出流
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(otherSocket.getOutputStream()));
printWriter.println(lineStr);
printWriter.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}