欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

javaweb,java并发编程实战书

墨初 知识笔记 93阅读

Computer network server service processing request class class Server realizes runnable {public void run () {try {server socket ss new server socket (port); And (! Thread.interrupted ()) new thread (new handler (ss.accept ())). start(); //or, single thread or thread pool} catch (ioexceptex) {/* . */}} handler handles logic class static class handler implements Runnable {final Socket socket (socket) {socket; } public void run(){ try { byte[]INPUT new byte[MAX _ INPUT]; socket.getInputStream()。 Read (input); Byte[] output process (input); socket.getOutputStream()。 Write (output); } catch (ioexception ex) {/* . */}} private byte [] process (byte [] command) {/* . */}} Advantages Each connection or request can be started in its own thread.

在独立的线程中并发处理。这样可以提高并发性和响应能力充分利用多核处理器的性能。

每个请求都可以在自己的线程中独立运行不会受到其他请求的影响互不干扰这样可以更好地利用系统资源提高响应速度和用户体验还可以避免一个请求的处理阻塞其他请求的处理提高系统的并发能力和吞吐量。

缺点

在使用多线程处理网络服务时需要考虑线程安全性和资源共享的问题。确保在多个线程之间正确管理和同步共享的数据和资源避免出现竞态条件和数据不一致的问题。

如果并发请求数量过大每个请求都启动一个独立的线程可能会导致系统资源的过度消耗和浪费。这可能会导致系统性能下降、响应时间延长甚至引发资源耗尽的问题。

可扩展性目标 平稳降级负载增加时

在面对负载增加的情况下为了保持系统的可用性和性能可以采取平稳降级的策略。这意味着在负载增加时系统可以根据优先级或其他规则逐渐降低某些功能或服务的优先级以确保核心功能的稳定性和性能。

持续改进随着资源增加

随着资源如CPU、内存、磁盘、带宽的增加可以持续改进系统的性能和可用性。这可以包括优化算法和代码、增加缓存、提高数据库查询效率等措施以更好地利用新增的资源并提供更好的用户体验。

满足可用性和性能目标

在设计和开发网络服务时需要明确可用性和性能目标并确保系统能够满足这些目标。这包括提供短延迟、满足需求、可调性的服务质量等。通过合理的架构设计、负载均衡、缓存和异步处理等技术手段可以实现这些目标。

分治法实现可伸缩性目标

分治法是一种有效的方法用于实现任何可伸缩性目标。通过将系统划分为多个独立的组件或服务并使用适当的通信和协调机制可以实现系统的可伸缩性。这样可以将负载分散到多个组件上提高系统的并发处理能力和可扩展性。

分治解决方案 将处理划分为小任务

为了实现高性能和可伸缩性可以将处理过程划分为小任务每个任务执行一个不受阻塞的操作。这样可以避免长时间的阻塞操作提高系统的并发能力和响应性。

事件驱动的执行

通过使用事件驱动的模型可以在每个任务被启用时执行它。通常一个IO事件作为触发器当有IO事件发生时相应的任务被调度和执行。这种方式可以提高系统的并发处理能力和资源利用率。

使用Java NIO的非阻塞机制

Java NIO提供了非阻塞的读写操作和感知IO事件的调度任务。通过使用Java NIO的非阻塞I/O API可以实现高性能的网络通信避免阻塞操作提高系统的并发性和响应能力。

事件驱动设计的无穷可能性

事件驱动的设计模式和机制具有无穷无尽的变化可能性。通过合理设计和实现事件驱动的架构可以满足不同的需求和场景提供高性能、可扩展和灵活的网络服务。

事件驱动 特点分析 资源损耗的减少

采用事件驱动的模型可以减少资源的消耗。相比于为每个客户端分配一个线程可以使用线程池或异步处理方式来管理并发连接和请求。这样可以减少线程的数量降低资源消耗并提高系统的并发能力。

更少的开销

事件驱动的模型可以减少上下文切换的次数因为每个事件处理程序在自己的线程中独立执行。此外可以使用非阻塞I/O操作和异步处理方式减少锁定操作和阻塞等待进一步降低开销。

调度速度的优化

为了提高调度速度可以采用高效的事件循环机制和调度算法。可以使用优先级队列、定时器和事件分发机制等技术优化事件的调度和处理过程提高系统的响应速度和效率。

类似于GUI事件驱动的操作

事件驱动的模型在处理网络服务时类似于GUI图形用户界面的事件驱动操作。可以借鉴GUI开发中的事件处理机制和设计模式将其应用于网络服务的事件驱动处理中以提高代码的可维护性和可扩展性。

问题分析

编程困难处理并发编程是一项具有挑战性的任务需要考虑多个线程或进程之间的交互和同步。这可能导致编程变得复杂和困难。

无法消除所有阻塞尽管反应堆模式可以提高系统的并发性和响应能力但仍然无法消除所有的阻塞。某些因素如垃圾回收和页面故障可能会导致一些阻塞情况的发生。

必须跟踪服务的逻辑状态在使用反应堆模式时必须跟踪服务的逻辑状态。这是因为反应堆模式依赖于事件的发生和处理需要确保正确地处理和分发事件以保持服务的一致性和正确性。

反应堆模式

反应堆模式和AWT线程类似都通过事件循环机制来调度和处理IO事件并将其分发给适当的处理程序。这种模式可以提高系统的并发性和响应能力。

管理绑定处理程序事件可以通过管理绑定处理程序事件的方式来实现事件的分发和处理。

处理程序执行非阻塞操作网络处理程序可以执行非阻塞的操作。

单线程模式

在反应堆模式中单线程模式是一种实现方式它使用单个线程来处理所有的IO事件和请求。在这种模式下所有的IO事件都被注册到一个事件循环中并由单个线程按顺序处理。

基本工作流程

注册IO事件所有的IO事件都被注册到事件循环中包括读取、写入、连接等操作。

事件循环单个线程按照注册的顺序循环遍历所有的IO事件并根据事件的类型进行相应的处理。

事件处理对于每个IO事件单线程模式会调用相应的处理程序来执行相应的操作例如读取数据、写入数据或建立连接。

非阻塞操作在单线程模式下处理程序执行非阻塞的操作以避免阻塞其他事件的处理。

事件分发处理程序根据事件的类型将结果分发给相应的处理程序或回调函数。

整体运行架构图借鉴Reactor官网图

单线程的反应堆模式通过使用Selector和ServerSocketChannel来处理IO事件和监听连接请求。它使用单线程来处理所有的事件并通过附加对象的方式将具体的处理逻辑委托给相应的处理程序。

根据上述架构图可以看出来当客户端请求过来直接请求到了就是Reactor对象我们来定义和开发一下Reactor类以及方法逻辑。

定义Reactor核心类

Reactor类这是主要的反应堆类实现了Runnable接口它包含一个Selector对象和一个ServerSocketChannel对象用于处理IO事件和监听连接请求。

class Reactor implements Runnable {final Selector selector;final ServerSocketChannel serverSocket;Reactor(int port) throws IOException {selector  Selector.open();serverSocket  ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(port));serverSocket.configureBlocking(false);SelectionKey sk  serverSocket.register(selector,SelectionKey.OP_ACCEPT);sk.attach(new Acceptor());} // class Reactor continuedpublic void run() { // normally in a newT hread try {while (!Thread.interrupted()) {selector.select();Set selected  selector.selectedKeys();Iterator it  selected.iterator();while (it.hasNext())dispatch((SelectionKey)(it.next());selected.clear();}} catch (IOException ex) { /* ... */ }}void dispatch(SelectionKey k) {Runnable r  (Runnable)(k.attachment());if (r ! null)r.run();}}
Reactor构造器

Reactor构造器中创建了一个Selector对象和一个ServerSocketChannel对象并将ServerSocketChannel绑定到指定的端口。然后将ServerSocketChannel注册到Selector上以便监听连接请求。同时将一个Acceptor对象附加到SelectionKey上用于处理接受连接的事件。

Acceptor类

这是一个内部类实现了Runnable接口。它用于处理接受连接的事件。在run方法中通过调用serverSocket.accept()来接受连接并创建一个新的Handler对象来处理该连接。

// class Reactor continuedclass Acceptor implements Runnable { // innerpublic void run() {try {SocketChannel c  serverSocket.accept();if (c ! null)new Handler(selector, c);}catch(IOException ex) { /* ... */ }}}}
run方法

通过调用selector.select()来等待IO事件的发生。一旦有事件发生就会获取到被选中的SelectionKey集合并遍历处理每个事件。在处理事件时调用dispatch方法来执行相应的处理程序。

dispatch方法

dispatch方法根据SelectionKey的附加对象获取到对应的处理程序并执行其run方法。这样可以将具体的处理逻辑委托给相应的处理程序。

Handler对象类

Handler是一个执行类实现了Runnable接口。它包含一个SocketChannel对象和一个SelectionKey对象用于与客户端进行通信。

final class Handler implements Runnable {final SocketChannel socket;final SelectionKey sk;ByteBuffer input  ByteBuffer.allocate(MAXIN);ByteBuffer output  ByteBuffer.allocate(MAXOUT);static final int READING  0, SENDING  1;int state  READING;Handler(Selector sel, SocketChannel c) throws IOException {socket  c; c.configureBlocking(false);// Optionally try first read nowsk  socket.register(sel, 0);sk.attach(this);sk.interestOps(SelectionKey.OP_READ);sel.wakeup();}void process() { /* ... */ }public void run() {try {if (state  READING) read();else if (state  SENDING) send();} catch (IOException ex) { /* ... */ }}void read() throws IOException {socket.read(input);if (inputIsComplete()) {process();state  SENDING;// Normally also do first write nowsk.interestOps(SelectionKey.OP_WRITE);}}void send() throws IOException {socket.write(output);if (outputIsComplete()) sk.cancel();}}
Handler构造函数

将传入的SocketChannel对象配置为非阻塞模式并将其注册到指定的Selector上。同时将Handler对象附加到SelectionKey上并设置对读取操作感兴趣。最后调用Selector的wakeup方法以确保Selector立即返回。

run方法

在run方法中根据当前的状态READING或SENDING来执行相应的操作。如果处于READING状态则调用read方法进行读取操作如果处于SENDING状态则调用send方法进行发送操作。

read方法

read方法用于从SocketChannel中读取数据。它将读取的数据存储到input ByteBuffer中并检查是否已经完整读取了所需的数据。如果数据已经完整读取则调用process方法进行处理并将状态设置为SENDING。同时将SelectionKey的兴趣操作设置为OP_WRITE以便在下一次循环中进行写入操作。

send方法

send方法用于向SocketChannel中写入数据。它将output ByteBuffer中的数据写入到SocketChannel中并检查是否已经完整发送了所有数据。如果数据已经完整发送则取消SelectionKey的注册。

对于单线程模式优缺点分析

优点单线程模式的优点是简单和易于实现因为只需要一个线程来处理所有的IO事件。

缺点无法充分利用多核处理器的性能并且可能存在性能瓶颈。在高并发或高负载的情况下可能需要考虑使用多线程或多进程的方式来处理IO事件。

对此我们进行改良从而出现了多线程的反应堆模式由于篇幅过长在本篇文章就暂时不进行介绍多线程模式了在下一篇文章【【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南 实现可伸缩IO专题— 下】会对多线程模式以及线程池模式、终极模式多个反应堆线程模式进行深入分析和介绍说明。

参考资料 Doug Lea官方主页

标签:
声明:无特别说明,转载请标明本文来源!