深入理解 Java 中的阻塞方法

在 Java 的并发编程世界中,阻塞方法是我们必须深入理解的核心概念。这些方法具有特殊的性质,它们会阻塞当前线程的执行,直到其操作完全完成。这意味着,线程会停下来等待,直到满足特定条件使其任务得以继续。正是这种天然的阻塞特性,赋予了它们“阻塞方法”的名称。

让我们看一个典型的例子:InputStreamread() 方法会一直阻塞,直到输入流中的数据被完全读取为止。除了这个,Java 中还有许多我们常见的阻塞方法:

  • InvokeAndWait(): 它会阻塞当前线程,直到事件分发线程执行完特定代码。
  • InputStream.read(): 它会持续阻塞,直到有输入数据可读、抛出异常或检测到流结束。
  • ServerSocket.accept(): 监听传入的 Java socket 连接,在此期间一直阻塞,直到建立连接。
  • CountDownLatch.await(): 导致当前线程等待,直到锁存器的计数减为零(除非线程被中断)。

虽然这些方法非常有用,但我们也必须认识到使用阻塞方法带来的几个潜在劣势:

  • 阻塞技术会对系统的可扩展性构成重大挑战。经典的阻塞解决方案通常采用多线程来服务多个客户端,以此作为缓解阻塞的一种手段。
  • 系统设计在这里显得尤为关键。因为即使是多线程系统,其能力也存在上限;设计不当的系统由于 JVM 线程数量的限制,可能仅能支撑几百或几千个线程。

实现示例:

让我们通过下面的例子来实际体验一下。在执行完第一个打印语句后,程序会在第二个打印语句处被“阻塞”。这是因为 read() 方法在等待输入,直到你在控制台输入一些字符并按下回车键,程序才会继续运行。

示例 1:

Java


CODEBLOCK_69d45f3c

输出

GFG
Geeks for Geeks

示例 2:

在这个例子中,我们将看到 INLINECODE9333c6d6 是如何工作的。当一个线程需要等待其他几个线程完成工作后才能开始时,这个方法非常有用。其中的 INLINECODEcbe2bcb1 方法负责减少计数,而 await() 方法则会阻塞线程,直到计数归零。

Java


// Java 程序演示阻塞方法

// 导入所有输入输出类

import java.io.*;

// 导入并发 CountDownLatch 类

// 来自 java.util 包

import java.util.concurrent.CountDownLatch;

// 类

class Main {

// Main 驱动方法

public static void main(String args[])

throws InterruptedException

{

// 创建一个任务,让它等待

// 四个线程完成后才开始

CountDownLatch latch = new CountDownLatch(4);

// 创建 Person 类型的线程

// 自定义参数输入

Person p1 = new Person(1500, latch, "PERSON-1");

Person p2 = new Person(2500, latch, "PERSON-2");

Person p3 = new Person(3500, latch, "PERSON-3");

Person p4 = new Person(4500, latch, "PERSON-4");

Person p5 = new Person(5500, latch, "PERSON-5");

// 启动线程

// 使用 start() 方法

p1.start();

p2.start();

p3.start();

p4.start();

p5.start();

// 等待四个线程

// 使用 latch.await() 方法

latch.await();

// 主线程已开始

System.out.println(Thread.currentThread().getName()

+ " has finished his work");

}

}

// 类 2

// 辅助类,继承 Thread 类

// 代表主线程等待的其他线程

class Person extends Thread {

// 此类的成员变量

private int delay;

private CountDownLatch latch;

// 此类的方法

public Person(int delay, CountDownLatch latch,

String name)

{

// super 指代父类

super(name);

// this 关键字指代当前对象本身

this.delay = delay;

this.latch = latch;

}

@Override public void run()

{

// Try 块检查异常

try {

Thread.sleep(delay);

latch.countDown();

// 打印当前线程名称

// 使用 getName() 方法

// 表示已完成工作

System.out.println(

Thread.currentThread().getName()

+ " has finished his work");

}

// Catch 块处理异常

catch (InterruptedException e) {

// 打印异常发生的行号

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/25834.html
点赞
0.00 平均评分 (0% 分数) - 0