JAVA TCP socket编程框架

用户:

java中做tcp socket编程的框架有哪些

元宝:

Java中常见的TCP Socket编程框架有以下几种:

1. Java原生Socket API

最基础的TCP编程方式

复制代码
// 服务端
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
​
// 客户端
Socket socket = new Socket("localhost", 8080);

2. Netty ⭐ (最流行)

  • 特点:异步、事件驱动、高性能

  • 适用场景:高并发服务器、RPC框架、游戏服务器

  • 优点:性能优异,社区活跃,文档丰富

  • GitHubhttps://github.com/netty/netty

复制代码
// Netty的简单服务器示例
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .childHandler(new ChannelInitializer() {
     @Override
     public void initChannel(SocketChannel ch) {
         ch.pipeline().addLast(new YourHandler());
     }
 });

3. Apache MINA

4. Grizzly

5. KryoNet (游戏开发常用)

6. Smart Socket

7. RPC框架的通信层

很多RPC框架底层也封装了TCP通信:

  • Dubbo:使用Netty作为默认通信框架

  • gRPC-Java:基于Netty

  • Apache Thrift:支持多种传输层

8. NIO.2 (AIO)

Java 7+的原生异步IO

复制代码
AsynchronousServerSocketChannel server = 
    AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<...>() {
    @Override
    public void completed(...) {
        // 处理连接
    }
});

选择建议:

场景 推荐框架 原因
高性能服务器 Netty 性能最好,生态完善
简单项目/学习 Java原生Socket 理解底层原理
游戏服务器 KryoNet 或 Netty 序列化高效
企业级应用 Netty 或 Grizzly 稳定可靠
快速开发 Smart Socket 简单易用

学习路径建议:

  1. 初学者:先学习Java原生Socket API,理解TCP基础

  2. 进阶:学习Netty,这是目前最主流的框架

  3. 专业方向:根据具体领域选择(如游戏用KryoNet)

Netty是目前最主流的选择,广泛应用于各种中间件和分布式系统中。

用户:

mina比原生socket多做了什么

元宝:

Apache MINA 本质上是对 Java NIO(非阻塞 I/O)的高级封装。相比原生 Socket(通常指 java.net.Socket),它最大的改变是从"过程式"的阻塞 I/O 升级为"事件驱动"的异步架构,帮你解决了高并发场景下的底层复杂性。

以下是 MINA 比原生 Socket 多做的核心工作:

1. 架构升级:从"阻塞线程"到"事件驱动"

这是最根本的区别。原生 Socket 是"一问一答"的阻塞模式,而 MINA 是"有消息才干活"的事件模式。

维度 原生 Socket (BIO) Apache MINA (NIO)
工作模式 阻塞 I/O:读不到数据线程就卡住 非阻塞 I/O:有数据到达才触发回调
线程模型 一连接一线程:并发高时线程爆炸 多路复用:少量线程处理大量连接
代码逻辑 主动调用 read() / write() 被动响应 messageReceived() 事件

优势:MINA 能轻松应对 C10K(万级并发)问题,而原生 BIO 在并发稍高时就会因线程资源耗尽而崩溃。

2. 内置"过滤器链":解耦协议处理

原生 Socket 通常需要手动处理粘包/拆包、编码解码(对象转字节流)。MINA 引入了类似 Servlet Filter 的**过滤器链(IoFilterChain)**机制:

  • 自动编解码 :通过 ProtocolCodecFilter 自动将字节流转换为 Java 对象(如按行读取、定长报文、自定义协议)。

  • 功能插拔:可以灵活插入日志、SSL 加密、心跳检测等过滤器,无需修改业务代码。

3. 完善的会话与生命周期管理

原生 Socket 连接建立后,你需要自己维护 Socket 对象和用户状态的映射。MINA 提供了:

  • IoSession :封装了连接上下文,自带属性存储(session.setAttribute()),方便管理用户状态。

  • 生命周期回调 :自动触发 sessionCreatedsessionIdle(空闲检测)、sessionClosed 等钩子函数,让你能精准控制连接状态。

4. 线程模型优化(SEDA)

原生 NIO 需要开发者自己管理复杂的 Selector 和多线程协作。MINA 帮你做好了:

  • 线程隔离:将"网络 I/O 线程"与"业务处理线程"分离,避免耗时业务阻塞网络读取。

  • SEDA 架构:支持分阶段事件驱动,允许你为不同阶段(如解码、业务逻辑)配置独立的线程池,提升 CPU 利用率。

5. 屏蔽底层差异与工具化

  • 传输层统一:同一套 MINA 代码可运行在 TCP、UDP 甚至串口(RS232)上,API 完全一致。

  • 监控与测试:内置 JMX 监控支持,以及 Mock 对象,使得网络层的单元测试变得可行。


总结:MINA 多做了什么?

如果你用原生 Socket,你需要自己写线程池、写 Selector 轮询、处理复杂的 ByteBuffer 切片、解决粘包问题。MINA 把这些"脏活累活"全做了 ,你只需要关注 IoHandler 中的业务逻辑(如 messageReceived 里处理消息)即可。

一句话 :MINA 是用事件驱动 + 过滤器链 + 会话管理,把复杂的 NIO 网络编程变成了简单的业务逻辑组装。

  • 引用 8 篇资料作为参考
  1. Apache MINA
  1. Apache MINA
  1. 大並發量 socket 通訊的解決方案
  1. MINA(基于 Java NIO 的高性能网络应用框架)
  1. 【网络开发】Apache MINA是一个网络应用框架使用介绍
  1. Apache MINA简单介绍及其使用
  1. Why MINA?
  1. 初识Apache MINA

用户:

MINA和Netty有什么区别?

元宝:

MINA 和 Netty 都是基于 Java NIO 的高性能网络框架,它们有相似的架构理念,但在很多关键方面有显著差异。这是两者的详细对比:

一、起源与背景

维度 Apache MINA Netty
创建时间 2004年 2008年
背景 Apache Directory 服务器的网络层 JBoss 内部项目,后独立
现状 维护相对较少,活跃度下降 高度活跃,持续演进

二、核心架构差异

1. 线程模型

复制代码
// MINA:默认是"一Acceptor多Processor"模型
NioSocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.setHandler(new MyIoHandler());
// MINA 2.x 后也支持类似 Netty 的 EventLoop

// Netty:明确的多级EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);  // 接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理IO
// 支持更精细的线程模型配置

关键差异

  • MINA:早期使用简化的线程模型,2.x 后向 Netty 看齐

  • Netty :从一开始就设计了精细的 EventLoopGroup,支持主从多 Reactor 模型

2. API 设计哲学

复制代码
// MINA:基于接口的回调
public class MyIoHandler extends IoHandlerAdapter {
    @Override
    public void messageReceived(IoSession session, Object message) {
        // 处理消息
    }
}

// Netty:基于 ChannelHandler 的管道
public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理消息
    }
}

关键差异

  • MINA:更偏向"框架"思维,定义了完整的生命周期

  • Netty:更偏向"工具库"思维,灵活组合各种组件

三、性能对比

维度 MINA Netty 说明
吞吐量 中等 Netty 在 Zero-Copy、内存池等方面优化更彻底
内存占用 较高 Netty 的 ByteBuf 内存池减少 GC 压力
延迟 中等 Netty 的事件循环和任务调度更高效
并发连接 万级 十万级+ Netty 能更好地应对 C10M 挑战

性能关键点

  • Netty 的 ByteBuf:支持池化、零拷贝、引用计数

  • MINA 的 IoBuffer:功能相对简单,性能优化较少

四、生态与社区

维度 MINA Netty
GitHub Stars ~1k ~**33k**
版本更新 较慢(最新 2.2.x) 快速(最新 4.x,已有 5.x alpha)
文档质量 一般 优秀(官方指南、示例丰富)
企业采用 较少 广泛(Dubbo、gRPC、Spark、Elasticsearch 等)

五、代码复杂度对比

简单的 Echo 服务器实现对比:

MINA 版本

复制代码
public class MinaEchoServer {
    public static void main(String[] args) throws Exception {
        NioSocketAcceptor acceptor = new NioSocketAcceptor();
        acceptor.setHandler(new IoHandlerAdapter() {
            @Override
            public void messageReceived(IoSession session, Object message) {
                // Echo
                session.write(message);
            }
        });
        acceptor.bind(new InetSocketAddress(8080));
    }
}

Netty 版本

复制代码
public class NettyEchoServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(boss, worker)
             .channel(NioServerSocketChannel.class)
             .childHandler(new ChannelInitializer() {
                 @Override
                 protected void initChannel(SocketChannel ch) {
                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                         @Override
                         public void channelRead(ChannelHandlerContext ctx, Object msg) {
                             ctx.write(msg);
                         }
                     });
                 }
             });
            b.bind(8080).sync().channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

复杂度分析

  • MINA:API 更简洁,但灵活性受限

  • Netty:稍显复杂,但配置更精确,控制力更强

六、扩展性与灵活性

特性 MINA Netty 说明
协议支持 HTTP、SSL 等基本协议 HTTP/2、WebSocket、Protobuf 等 Netty 的编解码器生态更丰富
传输层 NIO、OIO、APR、串口 NIO、OIO、Epoll、KQueue、Local Netty 支持更多原生传输
粘包处理 内置常用解码器 丰富的编解码器库 Netty 的 LengthFieldBasedFrameDecoder 等更强大
SSL/TLS 支持 支持且性能更好 Netty 的 OpenSSL 绑定性能更高

七、学习曲线

维度 MINA Netty
入门难度 较低 中等
精通难度 中等 较高(概念更多)
调试难度 较低 较高(异步回调堆栈)
最佳实践 资料较少 资料丰富(书籍、博客、视频)

八、实际场景选择建议

选择 MINA 当:

  1. 遗留系统维护:已有系统基于 MINA

  2. 简单内部系统:并发不高(< 1000 连接)

  3. 学习 NIO 原理:MINA 代码相对简单,适合理解 NIO 框架设计

  4. 嵌入式/特殊环境:MINA 支持串口等特殊传输

选择 Netty 当:

  1. 高性能要求:需要处理高并发、低延迟

  2. 生产系统:需要稳定性和社区支持

  3. 复杂协议:需要 HTTP/2、MQTT、Redis 等现成协议支持

  4. 微服务/RPC 框架:作为底层通信框架

  5. 长远考虑:技术生态和人才储备

九、历史演变

一个有趣的视角是:Netty 的部分设计思想源于对 MINA 的改进

  • Netty 3 在很多方面借鉴了 MINA

  • Netty 4 进行了重大重构,性能大幅提升

  • Netty 5 尝试引入更激进的异步模型(后暂停,现重启)

MINA 2.x 也借鉴了 Netty 的一些设计,如类似 EventLoop 的模型。

总结对比表

维度 胜出方 原因
性能 Netty 内存池、零拷贝等优化更彻底
生态 Netty 被众多知名项目采用
社区 Netty 活跃度高,更新快
易用性 MINA API 更简洁,概念更少
灵活性 Netty 组件化设计,扩展性更强
学习资源 Netty 文档、书籍、教程丰富

最终建议

对于绝大多数新项目,选择 Netty 是更明智的决定,因为:

  1. 性能优势明显

  2. 社区活跃,问题容易解决

  3. 人才市场上 Netty 技能需求更大

  4. 有更多现成的协议实现和最佳实践

MINA 更适合作为学习 NIO 框架设计的"教学案例",或者维护已有的 MINA 老系统。

用户:

netty并发连接10万+级 是指单个个实例单机吗 c10m是什么意思

元宝:

是的,Netty 的 10 万+ 并发连接通常指的是单个实例单机。这里的核心概念是:

1. C10M 的含义

C10M 是 "10 Million" 的缩写,即单机处理 1000 万并发连接的能力。

这是一个网络性能的里程碑级别:

  • C10K:单机处理 1 万并发连接(2000 年代的目标)

  • C10M:单机处理 1000 万并发连接(现代高性能服务器的目标)

2. 为什么 Netty 能支持 10 万+ 连接

技术基础

复制代码
# Netty 实现高并发的核心技术点

class NettyHighPerformance:
    def __init__(self):
        self.key_features = {
            "1. 事件驱动架构": "基于NIO,避免为每个连接创建线程",
            "2. 零拷贝技术": "减少内存拷贝,直接在内核和用户空间传输数据",
            "3. 内存池优化": "ByteBuf 对象池化,减少GC压力",
            "4. 高效线程模型": "主从 Reactor 多线程模型",
            "5. 高效的序列化": "Protobuf 等二进制协议支持"
        }
        
    def get_connection_scales(self):
        return {
            "传统BIO": "~1000 connections",  # 线程受限
            "普通NIO": "~10000 connections",  # C10K级别
            "Netty优化": "~100000+ connections",  # 10万+级别
            "极致优化": "~1000000+ connections"  # C1M级别
        }

3. 10 万+ 连接的实现原理

内存占用分析

每个连接的核心资源占用:

  1. 文件描述符:~1KB 内核内存

  2. TCP 缓冲区:接收/发送缓冲区各约 4KB

  3. 应用层内存:Netty 的 Channel 对象约几百字节

实际配置示例

4. 系统级优化要点

操作系统配置(Linux)

6. 实际测试数据参考

场景 连接数 内存占用 CPU 使用率 网络吞吐 延迟
简单 Echo 10 万 2-4 GB 20-30% 1-2 Gbps <1ms
聊天服务器 10 万 4-8 GB 30-50% 500 Mbps 1-5ms
游戏服务器 1 万 1-2 GB 40-60% 100 Mbps <10ms

7. 注意事项

  1. 硬件要求
  • 足够的内存(至少 16GB+)

  • 高性能网卡(万兆/25G)

  • 多核 CPU(8 核+)

  1. 应用设计
  • 避免阻塞操作

  • 使用连接池

  • 合理设计协议(如 protobuf)

  1. 监控指标
  • 连接建立速率

  • 内存使用趋势

  • GC 频率和时长

  • 网络吞吐量

总结

  • 10 万+ 连接:Netty 单机完全可达,需要合理配置

  • C10M:是更高阶的目标,需要系统级、硬件级的深度优化

  • 实际选择:大多数业务场景 10 万连接已足够,C10M 主要用于特殊的高性能场景(如 CDN、负载均衡器等)

关键技术演进路径: 传统 Socket → NIO/Netty(C10KC100K)→ 内核旁路技术(C1MC10M)

用户:

池化的话 这些对象就不用gc去管理了 池自己管理了吗

元宝:

是的,你理解得非常准确!池化的核心思想就是"自己管理对象生命周期,避免GC介入"。让我详细解释这个机制:

1. 对象池的工作原理

传统 GC 模式 vs 池化模式

复制代码
// 传统模式:频繁创建/销毁,GC负担重
for (int i = 0; i < 10000; i++) {
    ByteBuf buffer = Unpooled.buffer(1024);  // 1. 创建对象
    // ... 使用 buffer
    // buffer 离开作用域 -> 等待GC回收
    // GC 频繁触发,STW 停顿影响性能
}

// 池化模式:复用对象,减少GC
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
for (int i = 0; i < 10000; i++) {
    ByteBuf buffer = allocator.buffer(1024);  // 1. 从池中获取
    // ... 使用 buffer
    buffer.release();  // 2. 释放回池,而不是等待GC
    // 下次直接从池中获取,无需新建对象
}

2. Netty 内存池的具体实现

核心数据结构

复制代码
// Netty 4.x 内存池的核心组件
public class PoolArena {
    // 1. 不同大小的内存块池
    private final PoolSubpage[] tinySubpagePools;    // 0-512B
    private final PoolSubpage[] smallSubpagePools;   // 512B-8KB
    private final PoolChunkList q050;  // 50% 使用率的内存块
    private final PoolChunkList q025;  // 25% 使用率的内存块
    private final PoolChunkList q000;  // 0% 使用率的内存块
    
    // 2. 线程本地缓存 (ThreadLocal Cache)
    private final MemoryRegionCache[] tinySubPageDirectCaches;
    private final MemoryRegionCache[] smallSubPageDirectCaches;
}

内存池的管理层次

复制代码
Netty 内存池结构
├── Arena(竞技场)
│   ├── 多个 Chunk(16MB 内存块)
│   │   ├── 2048 个 Page(8KB)
│   │   │   ├── 多个 SubPage(< 8KB)
│   │   │   │   └── 多个 Element(最小单位,如 16B, 32B, 64B...)
│   │   └── 通过 Buddy Allocation 算法管理
│   └── 支持并发访问(多生产者-多消费者)
├── ThreadLocal Cache(线程本地缓存)
│   └── 避免线程竞争,提高分配速度
└── 回收队列
    └── 对象引用计数为 0 时加入队列,等待复用

3. 对象生命周期管理

引用计数机制

复制代码
public class PooledByteBuf extends AbstractReferenceCountedByteBuf {
    private int handle;           // 在 Chunk 中的位置
    private T memory;             // 底层内存
    private int offset;           // 偏移量
    private int length;           // 长度
    
    // 关键:引用计数
    private volatile int refCnt = 1;
    
    @Override
    public ByteBuf retain() {
        // 增加引用计数
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, 1);
            }
            if (refCnt == Integer.MAX_VALUE) {
                throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
            }
            if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
                break;
            }
        }
        return this;
    }
    
    @Override
    public boolean release() {
        // 减少引用计数
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, -1);
            }
            
            if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
                if (refCnt == 1) {
                    // 引用计数为 0,回收到池中
                    deallocate();
                    return true;
                }
                return false;
            }
        }
    }
    
    protected void deallocate() {
        if (handle >= 0) {
            // 不是归还给 GC,而是归还给内存池!
            long handle = this.handle;
            this.handle = -1;
            memory = null;
            chunk.arena.free(chunk, handle, maxLength, cache);
        }
    }
}

4. 为什么池化能减少 GC?

传统模式的问题

复制代码
应用程序
   ↓
创建对象 → 使用 → 废弃 → 触发 GC
   ↓
Full GC 暂停应用(Stop-The-World)

池化模式的优化

复制代码
应用程序
   ↓
从池中获取 → 使用 → 放回池中
   ↑            ↓
对象池 ← 引用计数为0 ←
    (无GC介入!)

5. 性能对比数据

场景 传统分配 池化分配 提升效果
创建速度 100 ns/次 20 ns/次 5 倍
GC 停顿 100-500 ms 1-10 ms 10-50 倍
内存碎片 严重 极少 显著改善
吞吐量 基准 提升 30-50% 大幅提升

6. Netty 池化的具体好处

内存层级

复制代码
// 1. DirectBuffer 池化(堆外内存)
ByteBuf directBuffer = allocator.directBuffer(1024);
// 使用完后自动回到池中,避免频繁的 DirectByteBuffer 分配/释放

// 2. HeapBuffer 池化(堆内内存)
ByteBuf heapBuffer = allocator.heapBuffer(1024);
// 同样复用,减少新生代 GC 压力

// 3. CompositeBuffer 池化
CompositeByteBuf composite = allocator.compositeBuffer();
// 组合多个缓冲区,避免数据拷贝

线程本地缓存(ThreadLocalCache)

复制代码
Thread 1         Thread 2         Thread 3
   │                 │                 │
   ▼                 ▼                 ▼
TinyCache       TinyCache       TinyCache
   │                 │                 │
   ▼                 ▼                 ▼
SmallCache      SmallCache      SmallCache
   │                 │                 │
   ▼                 ▼                 ▼
NormalCache     NormalCache     NormalCache
   │                 │                 │
   ▼                 ▼                 ▼
共享 Arena       共享 Arena       共享 Arena

优势

  1. 无锁分配:线程内分配无需加锁

  2. 减少竞争:避免多线程竞争全局资源

  3. 提高缓存命中率

7. 如何正确使用池化

正确示例

复制代码
// 1. 使用 PooledByteBufAllocator
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
ByteBuf buffer = allocator.buffer(1024);
try {
    // 使用 buffer
    buffer.writeBytes(data);
    // ...
} finally {
    // 2. 必须手动释放!
    buffer.release();
}

// 3. 引用计数传递
public void process(ByteBuf buf) {
    buf.retain();  // 增加引用计数
    // 异步处理
    channel.writeAndFlush(buf).addListener(future -> {
        buf.release();  // 处理完后释放
    });
}

常见陷阱

复制代码
// 错误:忘记释放,导致内存泄漏
ByteBuf buffer = allocator.buffer(1024);
// 使用后忘记 buffer.release();

// 错误:过早释放
ByteBuf buffer = allocator.buffer(1024);
buffer.release();
// 之后再次使用 buffer -> 抛出 IllegalReferenceCountException

// 错误:错误地使用引用
ByteBuf buffer = allocator.buffer(1024);
someList.add(buffer);
// 列表持有引用,但不知何时释放

8. 监控和调试

复制代码
// 查看内存池状态
PooledByteBufAllocator allocator = (PooledByteBufAllocator) PooledByteBufAllocator.DEFAULT;
​
// 获取详细统计信息
String dump = allocator.dumpStats();
System.out.println(dump);
// 输出类似:
// PooledByteBufAllocator(directArenas: 4, heapArenas: 4)
//   DirectArena(0):
//   Normal allocations: 1024
//   Small allocations: 512
//   Tiny allocations: 256
//   Active bytes: 2.5 MB
//   Free chunks: 3

总结

池化的本质是"以空间换时间,以管理换GC"

  1. 对象复用:避免频繁创建/销毁,减少内存分配开销

  2. GC 友好:对象生命周期由池管理,显著减少 GC 频率和停顿

  3. 缓存友好:对象连续分配,提高 CPU 缓存命中率

  4. 可预测性:内存使用更可控,避免内存碎片

关键要点

  • 池化不是"无GC",而是"少GC"和"可控GC"

  • 需要手动管理对象生命周期(引用计数)

  • 适合生命周期短、创建频繁的对象

  • Netty 默认启用了池化,是高性能的关键之一

适用场景

  • 高频创建的对象(ByteBuf、数据库连接、线程等)

  • 内存敏感的应用

  • 对延迟要求高的系统(减少 GC 停顿)

相关推荐
huangdong_17 小时前
电商平台图片URL原图转换技术深度解析:从缩略图到高清原图的完整方案
java·后端·spring
記億揺晃着的那天18 小时前
Java 调用外部 Go 程序的实践:ProcessBuilder 在生产环境中的应用
java·golang·processbuilder
JAVA面经实录91718 小时前
Java 数据结构与算法 (终极完整学习文档)
java·数据结构·算法
JAVA面经实录91718 小时前
操作系统面试题
java·服务器·数据库·计算机网络·面试
一杯奶茶¥19 小时前
基于springboot的失物招领管理系统带万字文档 校园失物招领管理系统 失物认领管理系统java springboot vue
java·vue.js·spring boot·java项目
不能只会打代码19 小时前
边缘视频分析平台的架构设计与性能优化——从750ms到190ms的调优之路
java·spring boot·redis·性能优化·边缘计算·物联网竞赛
小刘|19 小时前
Spring AI Alibaba 集成和风天气 API 实战
java·服务器·前端
KANGBboy19 小时前
java知识五(继承)
java·开发语言
AI人工智能+电脑小能手19 小时前
【大白话说Java面试题 第117题】【并发篇】第17题:线程有几种状态,之间如何转换?
java·开发语言·面试
DIY源码阁19 小时前
JavaSwing饮品管理系统 - MySQL版
java·数据库·mysql·eclipse