4 java多线程和java网络编程

主题 核心概念/组件 关键类/接口 主要特点/用途
多线程创建 继承 Thread 类
“Thread” 简单直接,但缺乏灵活性(Java 单继承)
实现 Runnable 接口 (推荐)
“Runnable” 更灵活,可实现多个接口,任务与线程分离
实现 Callable 接口 (带返回值)
“Callable”,
“Future” 任务可返回结果,并能抛出异常
线程管理 线程池
“ExecutorService”,
“Executors” 管理和重用线程,提升性能与资源利用率
线程同步 synchronized 关键字 (内置锁)
“synchronized” 保证方法或代码块的原子性、可见性和有序性,JVM 实现锁升级
ReentrantLock (显式锁)
“ReentrantLock”,
“Lock” 可中断、可设置公平性、支持条件变量,提供更灵活的同步控制
volatile 关键字
“volatile” 保证变量修改的可见性和有序性,但不保证原子性
线程通信 wait() / notify() / notifyAll()
“Object” 类的方法 需在 synchronized 块中使用,用于线程间协调
条件变量 (Condition)
“Condition” 与
“ReentrantLock” 配合,实现更精确的线程等待与唤醒
网络编程基础 TCP 协议
“Socket”,
“ServerSocket” 面向连接、可靠传输
UDP 协议
“DatagramSocket”,
“DatagramPacket” 无连接、高效、尽最大努力交付
HTTP 通信 HttpURLConnection
“HttpURLConnection” JDK 标准库的 HTTP 客户端
HttpClient (Java 11+)
“HttpClient” 现代、支持异步的 HTTP 客户端
高级主题 读写锁 (Read-Write Lock)
“ReentrantReadWriteLock” 读共享,写独占,适合读多写少场景
原子变量 (Atomic Variables)
“AtomicInteger”,
“AtomicLong” 基于 CAS,保证单个变量操作的原子性,无需加锁
并发集合 (Concurrent Collections)
“ConcurrentHashMap”,
“CopyOnWriteArrayList” 线程安全的集合容器

💡 多线程编程详解

一、线程创建与管理

Java 中创建线程主要有三种方式:

  1. 继承
    “Thread” 类:重写
    “run()” 方法。简单但缺乏灵活性,因为 Java 不支持多继承。
  2. 实现
    “Runnable” 接口(推荐):将任务与线程分离,更灵活,可以避免单继承的限制。
  3. 实现
    “Callable” 接口:允许线程执行后返回结果(通过
    “Future” 获取),并且可以抛出异常。

线程池是管理线程的最佳实践,它能有效避免频繁创建和销毁线程的开销,提升性能。

二、线程同步:保障线程安全

当多个线程访问共享资源时,为防止数据竞争(Race Condition)导致的数据不一致,需要进行同步。

“synchronized” 关键字(内置锁):

  • 用于修饰方法或代码块,确保同一时刻只有一个线程能执行该段代码。
  • 原理:基于 Java 对象内部的 Monitor 机制(每个对象都有一个关联的 Monitor)。锁有一个升级过程,旨在减少开销:偏向锁 -> 轻量级锁 (CAS) -> 重量级锁。
  • 它保证了原子性(锁内操作不可分割)、可见性(解锁前将变量刷回主内存)和有序性(防止指令重排序)。

“ReentrantLock”(显式锁):


  • “java.util.concurrent.locks.Lock” 接口的一个实现,提供了比
    “synchronized” 更丰富的功能:
    • 可中断:
      “lockInterruptibly()” 允许在等待锁时响应中断。
    • 尝试非阻塞获取锁:
      “tryLock()” 尝试获取锁,成功返回 true,失败返回 false而不一直等待。
    • 公平性:可以创建公平锁(按申请顺序获取锁)或非公平锁。
    • 多个条件变量(
      “Condition”):一个锁可以关联多个条件队列,实现更精细的线程等待与唤醒(如生产者-消费者模型)。
  • 注意:必须手动在
    “finally” 块中调用
    “unlock()” 释放锁。

“volatile” 关键字:

  • 确保变量的修改对所有线程立即可见(解决可见性问题),并禁止指令重排序(解决有序性问题,例如保障双重检查锁定的单例模式正确性)。
  • 局限性:它不保证复合操作的原子性(如
    “count++”)。适合用作状态标志位。

三、线程间协作

“wait()” /
“notify()” /
“notifyAll()”:


  • “Object” 类的方法,必须在
    “synchronized” 同步块或同步方法内使用。

“wait()”:释放当前持有的锁,使线程进入等待状态。
*
“notify()”/
“notifyAll()”:唤醒在此对象监视器上等待的单个或所有线程。
2.
“Condition” 接口:


  • “ReentrantLock” 配合使用,通过
    “await()”、
    “signal()”、
    “signalAll()” 方法实现类似的等待/通知机制,且可以绑定多个条件,更灵活。

四、高级工具与最佳实践

  • 读写锁 (
    “ReentrantReadWriteLock”):允许多个线程同时读,但写线程独占。非常适合读多写少的场景。
  • 原子变量类:如
    “AtomicInteger”,通过 CAS (Compare-And-Swap) 操作保证单个变量的更新具备原子性,性能通常优于锁。
  • 并发集合:优先使用
    “ConcurrentHashMap”、
    “CopyOnWriteArrayList” 等线程安全的容器,而非自行用锁同步普通集合。
  • 避免死锁:确保多个线程以一致的顺序获取锁;使用
    “tryLock()” 尝试获取锁并设置超时时间。

🌐 网络编程详解

一、核心概念

  • IP 地址与端口:IP 标识网络中的设备,端口(0-65535)标识设备上的具体应用程序。
  • TCP (Transmission Control Protocol):面向连接、可靠的字节流协议。通过三次握手建立连接,保证数据顺序和正确送达。适用于文件传输、网页浏览等。
  • UDP (User Datagram Protocol):无连接、不可靠的数据报协议。发送数据包但不保证顺序和送达。开销小,速度快,适用于音视频通话、直播等对实时性要求高的场景。

二、TCP 编程

“Socket”:客户端使用,用于连接指定服务器和端口。
*
“ServerSocket”:服务端使用,在指定端口监听,接受客户端的连接请求。

  • 通信基于 IO 流 (
    “InputStream”/
    “OutputStream”)。

三、UDP 编程

“DatagramSocket”:用于发送和接收数据报的套接字。
*
“DatagramPacket”:表示一个数据包,包含了数据、长度、目标地址和端口。

四、HTTP 编程

“HttpURLConnection” (传统方式):JDK 自带的 HTTP 客户端,可用于发送 GET、POST 等请求。
*
“HttpClient” (Java 11+ 推荐):现代、支持异步和 HTTP/2 的 HTTP 客户端 API,更强大和灵活。

五、网络编程最佳实践

  1. 使用 try-with-resources:确保 Socket、Stream 等资源在使用完毕后被正确关闭,避免资源泄漏。
  2. 设置超时:使用
    “setSoTimeout()” 等方法设置连接和读取超时,防止网络故障导致线程无限期阻塞。
  3. 处理异常:网络操作可能抛出
    “IOException”、
    “UnknownHostException” 等,必须妥善处理。
  4. 使用缓冲流:用
    “BufferedReader”/
    “BufferedWriter” 包装字节流,提高 I/O 效率。
  5. 服务端使用多线程或线程池:为每个接受的客户端连接分配一个独立的线程或交给线程池处理,以支持并发。

🧩 多线程与网络编程的结合

在实际网络应用中,多线程至关重要:

  • 服务器端:通常是多线程的。主线程(
    “ServerSocket.accept()”)循环等待客户端连接。一旦有新连接到达,就创建一个新的工作线程(或从线程池获取)来处理这个客户端的 Socket 通信(读写数据),从而同时服务多个客户端。

// 服务端多线程处理示例片段
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while (true) {
Socket clientSocket = serverSocket.accept(); // 主线程接受连接
// 为每个客户端创建一个新线程进行处理
new Thread(() -> {
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
String request = in.readLine();
// … 处理请求并响应 …
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}

⚠️ 注意事项

  1. 性能与开销:线程的创建、销毁和上下文切换有开销。切忌为每个任务盲目创建新线程,应使用线程池。
  2. 复杂度与调试:多线程程序 bug 往往难以稳定复现和调试。设计时应清晰界定临界区,尽量减少共享数据,优先使用不可变对象和线程安全容器。
  3. 可靠性:网络是不稳定的。代码必须充分考虑超时、重连、断线重连、异常处理等机制。
  4. 安全性:涉及敏感数据时,应考虑使用 SSL/TLS 加密网络通信(如 HTTPS)
posted @ 2025-09-03 12:53  行路客  阅读(10)  评论(0)    收藏  举报  来源