Java学习小记——多线程&Socket编程

这篇具有很好参考价值的文章主要介绍了Java学习小记——多线程&Socket编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

线程池

线程池介绍

在创建一个线程时存在一定的开销,创建线程的开销相比于一般的算法来说较大。首先需要建立一个调用栈,接着操作系统还需要建立很多数据结构来维护线程的状态等等。为了解决每次都需要临时创建线程带来开销的问题,引入了线程池。

线程池会预先建立好线程,等待任务派发。一个线程池内可能会有几十上百个闲置的线程,当有任务来临时,需要选中一个线程执行任务。执行完毕后释放该线程资源,重新回到闲置状态,等待下一个任务。

在线程池中,通常有一个Blocking Queue用来派发任务。队列通常为空,当有新的任务进队列时,闲置线程开始抢占该任务。当没有闲置线程时,新任务到达Blocking Queue便会开始排队,等待有线程空出来。

线程池的参数

corePoolSize:线程池中初始线程的数量,可能处于等待状态
maimumPoolSize:线程池中最大允许线程数量
keepAliveTime:超出corePoolSize部分线程如果等待这些时间,就会被回收

Java线程池ExecutorTester

可以使用ThreadPoolExecutor创建线程池:
Java学习小记——多线程&Socket编程,java,学习
参数包含了corePoolSize,maimumPoolSize,keepAliveTime,时间单元TimeUnit以及一个可执行的workQueue任务队列。

另一种写法是用executors.newFixedThreadPool()创建线程池。这样的写法参数较少,只需要指定一个线程数即可。

ExecutorService executor = Executors.newFixedThreadPool(3);

如此创建的线程池,拥有的线程数量固定为3,如果任务数大于3,那么多余的任务则只有排队等待。

接下来我们一口气给这个线程池派发10个任务试试:

for (int i = 0; i < 10; i++) {
	executor.submit(new CodingTask(i));
}
System.out.println("10 tasks dispatched successfully.");

Java学习小记——多线程&Socket编程,java,学习
由于线程池参数为3,所以会按3个一组来抢占任务。
Java学习小记——多线程&Socket编程,java,学习
第一轮派发完毕后:
Java学习小记——多线程&Socket编程,java,学习
可以看到将3个任务派发给了线程们,workQueue中还剩7个任务。

Future<?>可以用来控制任务的执行:
Java学习小记——多线程&Socket编程,java,学习
Java学习小记——多线程&Socket编程,java,学习
cancel(boolean):可以临时中止执行任务
get():若成功执行则返回null,否则等待
iscancelled():获取任务是否中止
isDone():获取任务是否成功执行

ExecutorService executor = Executors.newFixedThreadPool(3);

    List<Future<?>> taskResults = new LinkedList<>();
    for (int i = 0; i < 10; i++) {
      taskResults.add(executor.submit(new CodingTask(i)));
    }
    System.out.println("10 tasks dispatched successfully.");

    for (Future<?> taskResult : taskResults) {
      taskResult.get();//get等待task结束,一旦结束会返回null
    }

服务器socket编程

普通socket编程

我们设想这样一个场景,一个服务器监听在6666端口上。若客户发aaa,则响应Hello aaa。首先我们需要建立一个服务端socket去监听端口号:

ServerSocket serverSocket = new ServerSocket(6666)

然后建立一个客户端socket,用getRemoteSocketAddress()方法获取远端socket端口号:

Socket clientSocket = serverSocket.accept();
System.out.println("Incoming connection from "
            + clientSocket.getRemoteSocketAddress());

最后用clientSoclet提供的getInputStream()方法和getOutputStream()方法实现开头我们所设想的功能:

try(ServerSocket serverSocket = new ServerSocket(6666)){
			System.out.println("listening on "+serverSocket.getLocalSocketAddress());
			Socket clientSocket = serverSocket.accept();
			System.out.println("Incoming connection from "+clientSocket.getRemoteSocketAddress());
			try(Scanner input = new Scanner(clientSocket.getInputStream())){
				String request = input.nextLine();
				System.out.println(String.format("Request from %s: %s",
						clientSocket.getRemoteSocketAddress(),
						request));
				String response = "Hello"+request+".\n";
				clientSocket.getOutputStream().write(response.getBytes());
			}
		}

执行以上代码并在命令行执行:

telnet localhost 6666

Java学习小记——多线程&Socket编程,java,学习
输入aaa后,服务端返回消息:
Java学习小记——多线程&Socket编程,java,学习
Java学习小记——多线程&Socket编程,java,学习
当然,正常的服务器不会在处理完一个请求后就马上切断与客户端的连接。所以,我们需要在把处理请求块放进一个循环中,并且加入终止条件即可。

try(ServerSocket serverSocket = new ServerSocket(6666)){
			System.out.println("listening on "+serverSocket.getLocalSocketAddress());
			Socket clientSocket = serverSocket.accept();
			System.out.println("Incoming connection from "+clientSocket.getRemoteSocketAddress());
			try(Scanner input = new Scanner(clientSocket.getInputStream())){
				while(true) {
					String request = input.nextLine();
					if(request.equals("quit")) {
						break;
					}
					System.out.println(String.format("Request from %s: %s",
							clientSocket.getRemoteSocketAddress(),
							request));
					String response = "Hello "+request+".\n";
					clientSocket.getOutputStream().write(response.getBytes());
				}
			}
		}

Java学习小记——多线程&Socket编程,java,学习
但是此时的服务器只能处理一个客户的请求。当另开一个控制台telnet 6666端口时,由于已经被占用,其他客户无法接受服务。

线程池并行处理客户请求

线程池在前面已经介绍过,它预先建立好了线程,等待任务来临。在线程池中有一个Blocking Queue,任务到来后会进入该队列,当到达队头并且有闲置进程时就会被选中执行。
Java学习小记——多线程&Socket编程,java,学习

在Java中,扔进Blocking Queue的是一个个Client Handler,因为每个client会占据一个线程,所以Client Handler即代表待执行的任务。线程调用Client Handle的run()方法来执行该任务。

	ExecutorService executor = Executors.newFixedThreadPool(3);
    RequestHandler requestHandler = new RequestHandler();

    try (ServerSocket serverSocket = new ServerSocket(7777)) {
      System.out.println("Listening on "
          + serverSocket.getLocalSocketAddress());

      while (true) {
        Socket clientSocket = serverSocket.accept();
        System.out.println("Incoming connection from "
            + clientSocket.getRemoteSocketAddress());
        executor.submit(
            new ClientHandler(clientSocket, requestHandler));
      }
    }

Java学习小记——多线程&Socket编程,java,学习

此时,服务器端就就可以支持多线程工作,因为线程池最大为3,所以最多可以同时开3个命令行访问服务器。当第4个命令行请求服务时,仍可连接成功,但需等待前3个服务中至少有一个服务退出,才可以接受服务。

Java NIO异步处理客户请求

每个线程处理一个客户请求看似十分合理,实则也存在缺点。因为线程池的容量有限,一个几十上百线程的线程池最多也只能服务几十上百个客户,难以实现大容量高吞吐的服务端。

在上一节的服务端线程池中,看似是所有线程都在忙着应付手头上的任务,而无暇顾及新到的任务,然而事实真的如此吗?比如当线程等待用户的输入时,用户的输入可以非常慢,这时线程便会被阻塞,并非在忙碌工作着。

因此,我们是否可以将所有client的input记住,再去检查谁输入完毕,就先去读这个输入并处理。也就是说,我们应该把一个个的request去交给线程处理,而不是把整个client都交给线程去处理。这样就避免了整个线程的等待时间,即将同步处理优化为了异步处理,这就是NIO异步服务器。
Java学习小记——多线程&Socket编程,java,学习

在NIO异步服务器中,等待的任务不再进入一个Blocking Queue,而是进入一个Channel列表。列表中有很多的Client,在每个循环开始时,通过一个Selector选择器去选择一个有数据的Channel,接着Request Handle去处理这个有数据的Channel,如此循环。文章来源地址https://www.toymoban.com/news/detail-831154.html

	ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(false);
    serverChannel.bind(new InetSocketAddress(8888));
    System.out.println("Listening on "
        + serverChannel.getLocalAddress());

    Selector selector = Selector.open();
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);

    ByteBuffer buffer = ByteBuffer.allocate(1024);
    RequestHandler requestHandler = new RequestHandler();
    while (true) {
      int selected = selector.select();
      if (selected == 0) {
        continue;
      }

      Set<SelectionKey> selectedKeys = selector.selectedKeys();
      Iterator<SelectionKey> keyIter = selectedKeys.iterator();

      while (keyIter.hasNext()) {
        SelectionKey key = keyIter.next();

        if (key.isAcceptable()) {
          ServerSocketChannel channel =
              (ServerSocketChannel) key.channel();
          SocketChannel clientChannel = channel.accept();
          System.out.println("Incoming connection from "
              + clientChannel.getRemoteAddress());
          clientChannel.configureBlocking(false);
          clientChannel.register(
              selector, SelectionKey.OP_READ);
        }

        if (key.isReadable()) {
          SocketChannel channel =
              (SocketChannel) key.channel();
          channel.read(buffer);
          String request = new String(buffer.array()).trim();
          buffer.clear();
          System.out.println(String.format(
              "Request from %s: %s",
              channel.getRemoteAddress(),
              request));
          String response = requestHandler.handle(request);
          channel.write(ByteBuffer.wrap(response.getBytes()));
        }

        keyIter.remove();
      }
    }

到了这里,关于Java学习小记——多线程&Socket编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA Socket编程

    Socket编程是在TCP/IP、UDP协议上的网络编程,在此之前,先了解下常见的网络模型: OSI七层模型与TCP模型 : OSI七层模型详解 (OSI七层模型详解) Socket就在应用程序的传输层和应用层之间的一个抽象层: 在计算机网络编程技术中,两个进程或者说两台计算机可以通过一个网络

    2024年02月07日
    浏览(21)
  • JAVA—socket编程

    socket编程,是在不同的进程间进行网络通讯的一种编程技术,在socket编程中基于TCP、UDP等协议做的一层封装,是一套系统所提供的的用于进行网络通信的编程接口。 客户端java代码:  服务端java代码: 先启动服务端server,再启动客户端client,等待提示连接成功后就可以测试发

    2024年02月11日
    浏览(22)
  • Java网络Socket编程-websocket

    实现一个用于监测 WebSocket 连接状态的线程类,其作用是通过创建一个 WebSocket 客户端,连接到指定的 WebSocket 地址,并监测连接的状态。 代码中的 WebSocketThread 类继承自 Thread ,意味着它可以在单独的线程中执行。该线程类使用 Tyrus 提供的 @ClientEndpoint 注解来标识这是一个

    2024年02月08日
    浏览(50)
  • Java 网络编程 —— Socket 详解

    在【客户端/服务端】的通信模式中,客户端需要主动构造与服务器连接的 Socket,构造方法有以下几种重载形式: 除了第一个不带参数的构造方法,其他构造方法都会试图建立与服务器的连接,一旦连接成功,就返回 Socket 对象,否则抛出异常 1. 设定等待建立连接的超时时间

    2024年02月01日
    浏览(36)
  • 【网络编程】Java中的Socket

    所谓Socket(套接字),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行

    2024年02月07日
    浏览(37)
  • 基于Java Socket写一个多线程的聊天室(附源码)

    Socket编程是在TCP/IP上的网络编程,但是Socket在上述模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来 ​ 我们可以发现Socket就在应用程序的传输层和应用层之间,设计了一个Socket抽象层,传输层的底一层的服务提供给Socket抽象层,S

    2024年02月10日
    浏览(54)
  • 【Java socket编程】多人聊天室

    课程设计大作业 功能: 1.具有 点对点通信 功能,任意客户端之间能够发送消息。 2.具有 群组通信 功能: 客户端可以 自主建立群组 ,添加或删除组成员; 客户端能够向组内成员同时发送消息,其他组成员不能收到。 3.具有 广播 功能 客户端能够向所有其他成员 广播消息

    2024年02月04日
    浏览(40)
  • Java——TCP UDP Socket编程

    目录 一、网络的相关概念 (一)网络通信 (二)网络 (三)ip地址 (四)ipv4地址分类 (五)域名 (六)网络通信协议 (七)TCP和UDP 二、InetAddress类 三、Socket 四、TCP网络编程 (一)案例一——使用字节流 (二)案例二——使用字节流  (三)案例三——使用字符流 (四

    2024年02月06日
    浏览(43)
  • JAVA--基于TCP协议的Socket编程

    目录         一、Socket类和ServerSocket类 1. Socket类  2. ServerSocket类 二、 使用TCP的 Socket编程实现登录功能 1. 实现单用户登录 2. 实现多客户端用户登录 3. InetAddress类         TCP具有很好的安全性能          速度较慢 (1)java.net包的两个类Socket和ServerSocket,分别用来实现

    2023年04月08日
    浏览(30)
  • 如何在Java实现TCP方式发送和接收Socket消息(多线程模式)

    在Java编程中,使用TCP协议进行Socket通信是非常常见的场景。本文将详细介绍如何在Java中实现TCP方式发送和接收Socket消息,并且利用多线程模式来提高通信效率。 首先,我们需要创建一个Server端来处理接收到的Socket连接请求。以下是实现的步骤: 创建一个ServerSocket对象,并指

    2024年02月12日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包