欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

订阅shiker.tech

文章发布订阅~

通过邮箱订阅文章更新,您将在文章发布时收到及时的邮件提醒~

代码之境:橙序员的JAVA漂流(五)
(last modified Feb 28, 2025, 9:30 PM )
by
侧边栏壁纸
  • 累计撰写 212 篇文章
  • 累计创建 69 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

代码之境:橙序员的JAVA漂流(五)

橙序员
2025-02-23 / 0 评论 / 0 点赞 / 284 阅读 / 3,161 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

第五章讲述了橙序员踏上并发峡谷的冒险旅程。他意识到并发编程中的多线程和竞态条件就像峡谷中奔腾的河流,必须通过同步机制来确保线程安全。他运用synchronized关键字来控制共享资源,避免了竞态条件的发生,确保线程访问顺序。随后,竞态秃鹫的出现象征着潜在的数据竞争问题,橙序员使用volatile关键字来防止内存可见性问题,在此过程中,他成功地击退了竞态秃鹫。随着深入峡谷,他遇到了代表线程状态的符文NEW、RUNNABLE和BLOCKED,意识到通过线程池来优化资源管理的重要性。最后,他利用wait/notify机制来实现线程之间的通信,确保了整个过程的高效协作。最终,橙序员成功收集符文,顺利通过峡谷的挑战,体现了并发编程中的各种关键技术。

第五章:并发峡谷

橙序员满怀壮志,再次踏上充满未知的征途,这次他的目的地是神秘而危险的并发峡谷。远远望去,这座峡谷宛如大地被撕裂开的一道狰狞裂缝,奔腾的河流在谷底汹涌咆哮,水面上不时涌起剧烈的波动,仿佛有无数潜藏的暗流在相互冲撞,空气中弥漫着一股令人不安的紧张气息。橙序员深知,这片峡谷是多线程和并发问题的具象化体现,是每一位程序员在技术之路上必须跨越的艰难险阻。

跨越 Thread 河流,并制服竞态秃鹫。” 手机屏幕适时弹出的任务提示,让他的神情瞬间变得凝重起来。

“Thread 河流?竞态秃鹫?” 橙序员微微一愣,很快便恍然大悟。Thread 是java中实现并发的基础单元,而竞态条件则是多线程编程中如影随形的致命隐患,这秃鹫不正象征着这种极具威胁的隐患吗?他心里清楚,要想在并发编程的复杂世界里避免数据竞争,就必须运用同步机制来保障线程安全。

深吸一口气,橙序员平复了一下紧张的心情,迈着坚定的步伐走进了峡谷,准备迎接这场充满挑战的冒险。

穿越 Thread 河流

橙序员来到河流岸边,只见湍急的河水中,成群的 “线程” 如同脱缰的野马,各自朝着不同的方向横冲直撞,速度极快且毫无秩序。每当两个线程靠得太近,河水便会剧烈翻腾,水花四溅,仿佛它们之间正在进行一场激烈的资源争夺战。

“这就是竞态条件。” 橙序员在心中默默念叨,“如果不加以有效控制,线程之间就会为了争夺共享资源而大打出手,导致不可预见的错误。” 他的脑海中迅速浮现出曾经遇到的一个常见并发问题:多线程同时访问共享资源时,若未进行加锁处理,就会造成资源的不一致性

为了制服这种混乱的竞争局面,他必须借助 synchronized 的力量,确保对共享资源的访问是有序的串行操作。他迅速从手机中调出相关代码,并将其具象化,只见手机屏幕上光芒闪烁,synchronized 锁链开始在 Thread 河流的两岸缓缓显现。

public class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

随着锁链的逐渐成型,河流中的混乱局面得到了有效控制。每一个试图访问共享count的线程,都必须先获取那至关重要的锁,完成操作后再释放,这就确保了同一时刻只有一个线程能够对count值进行修改。渐渐地,河流中的波动逐渐平息,线程不再肆意碰撞,线程同步机制成功避免了竞态条件的发生,Thread 河流变得平静而有序。

竞态秃鹫的出现

就在橙序员准备继续前行时,突然,一声尖锐的鸣叫划破长空,一只巨大的竞态秃鹫如黑色的闪电般从天而降,径直扑向他面前的资源,其犀利的眼神中透露出对资源的贪婪。

“竞态秃鹫!” 橙序员瞬间警觉起来,全身的神经都紧绷着。他心里明白,这只秃鹫代表着并发编程中的竞态问题,一旦资源访问出现不当,它就会像幽灵般突然出现,带来难以预料的错误。

他迅速拿出手机,毫不犹豫地选择了 “volatile 的闪光信标” 来应对这场危机。volatile关键字就像是一把神奇的钥匙,能够保证多线程之间的内存可见性,即当一个线程修改了共享变量时,其他线程能够在第一时间看到修改后的结果,避免了线程之间因为 “视野错乱” 而产生的问题。

public class SharedResource {
    private volatile boolean flag = false;

    public void toggleFlag() {
        flag =!flag;
    }

    public boolean getFlag() {
        return flag;
    }
}

刹那间,屏幕上一道橙色的闪光信标亮起,光芒照亮了整个峡谷。竞态秃鹫在这耀眼信号的干扰下,逐渐迷失了方向,开始远离共享资源。通过使用volatile,所有线程都能确保读取到最新的变量值,成功避免了内存的缓存问题,从而消除了竞态秃鹫带来的威胁。

线程状态:NEW/RUNNABLE/BLOCKED

橙序员继续小心翼翼地前行,峡谷深处,几座巨大的石门突兀地出现在眼前,每一座门上都刻着不同的符文:NEW、RUNNABLE、BLOCKED,这些符文将峡谷重的大门牢牢锁住,想要走出峡谷,就得将这些符文一一收集。

“这些符文代表着线程的不同状态。” 橙序员在心中默默回忆,“NEW 表示线程刚刚被创建,尚未启动;RUNNABLE 表示线程已经准备好执行,正处于就绪队列中跃跃欲试;BLOCKED 则表示线程由于获取锁失败而被暂时阻塞,无法继续执行。”

他缓缓走近符文,手机屏幕上立刻显示出相应的线程状态变化说明:

  • NEW:线程创建时处于此状态,就像一个蓄势待发的运动员,还未踏上起跑线。

  • RUNNABLE:线程准备执行,如同站在起跑线上,只等一声令下就全力冲刺,处于就绪队列中。

  • BLOCKED:线程由于获取锁失败而被阻塞,就像运动员在比赛中遇到了障碍物,无奈进入阻塞队列等待。

“我必须确保线程能够在正确的状态下流转。” 橙序员紧锁眉头,陷入了沉思,片刻后,他决定通过使用线程池来优化资源的使用,提高线程管理的效率。

线程池:优化资源消耗

为了避免频繁地创建和销毁线程,造成系统资源的大量浪费,橙序员决定借助线程池这一强大的工具来管理线程。线程池就像是一个高效的调度中心,通过复用线程,大大减少了系统资源的开销,避免了过度的线程创建和销毁所带来的性能损失。

他在手机上熟练地输入了以下代码:

ExecutorService executor = Executors.newFixedThreadPool(10);

for (int i = 0; i < 100; i++) {
    executor.submit(() -> {
        System.out.println(Thread.currentThread().getName() + " is executing");
    });
}

executor.shutdown();

随着代码的输入完成,屏幕上,线程池的机械装置开始有条不紊地运作起来。十个线程被合理地管理和分配任务,每个线程在完成任务后并不会被轻易销毁,而是被回收复用,就像训练有素的士兵,随时准备接受新的任务。通过线程池,橙序员成功将线程符文一一收集,最终通过了峡谷的符文大门。

线程通信:wait/notify 的烽火信号系统

橙序员继续向峡谷深处进发,一座座高大的信号塔突然出现在眼前,只见塔上挂着一面巨大的烽火旗,旗帜时而缓缓升起,时而迅速降落,仿佛在传递着某种神秘的信息,这些信号塔的尽头连接这本次峡谷的出口大门。

“看来我不得不修复这些信号塔了,等等,我可以用wait/notify 机制。” 橙序员心中一动,瞬间意识到这其中的奥秘。wait()notify()就像是线程之间沟通的桥梁,用于协调多个线程之间的通信。wait()线程进入等待状态,就像烽火台下的士兵,静静地等待着信号;直到接收到另一个线程的通知,即notify(),才会被唤醒,重新投入行动。

他通过手机输入了一个小例子:

class SharedResource {
    private boolean ready = false;

    public synchronized void waitForSignal() throws InterruptedException {
        while (!ready) {
            wait();
        }
        System.out.println("Thread received the signal!");
    }

    public synchronized void sendSignal() {
        ready = true;
        notify();
    }
}

当一个线程调用wait()时,它就像烽火台下待命的士兵,进入等待状态,直到接收到信号(notify())才会被唤醒。这就像是一个精心设计的烽火信号系统,能够确保线程间的协调和资源共享,避免了线程资源的无谓浪费。这些信号塔在wait/notify 机制下,逐个将开门信号传递到出口大门,最终峡谷出口缓缓打开~

神秘商人现身

橙序员正沉浸在打开峡谷出口的喜悦中,突然,神秘商人那熟悉的身影出现在峡谷的尽头,他面带微笑,步伐轻盈地朝着橙序员走来。

“看来你已经掌握了并发编程的基本机制。” 商人的声音温和而亲切,“不过,现代并发编程的挑战远不止这些。你是否听说过虚拟线程?”

“虚拟线程?” 橙序员眼中闪过一丝好奇,迫不及待地问道。

商人递给他一颗闪烁着奇异光芒的水晶,上面清晰地写着:“虚拟线程(Loom 项目),它能够以更轻量级的方式管理并发,提高性能。” 接着商人开始讲解:“虚拟线程是一种革命性的技术,它可以让你轻松创建大量线程,而不用担心高昂的资源开销。比如说,传统的线程创建和管理方式可能会因为资源有限而受到限制,但虚拟线程就不一样。假设我们要处理大量的用户请求,使用传统线程可能会像这样:”

// 传统线程处理大量用户请求示例
for (int i = 0; i < 10000; i++) {
    Thread thread = new Thread(() -> {
        // 处理用户请求的逻辑
        System.out.println("Handling user request in traditional thread: " + Thread.currentThread().getName());
    });
    thread.start();
}

“这样的代码在创建大量线程时,很容易导致资源耗尽,程序崩溃。而使用虚拟线程(这里以 JDK 21 的虚拟线程示例代码),情况就大不相同:”

// 虚拟线程处理大量用户请求示例
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 10000; i++) {
    executor.submit(() -> {
        // 处理用户请求的逻辑
        System.out.println("Handling user request in virtual thread: " + Thread.currentThread().getName());
    });
}
executor.shutdown();

“你看,通过虚拟线程,我们可以轻松创建数以万计的线程,它们占用的资源极少,却能高效地处理任务,大大提升了程序的并发处理能力。” 商人耐心地解释道。

“这将是并发编程的新革命。” 商人微笑着说完,便如一阵清风般消失在峡谷深处。

橙序员紧紧握着手中的水晶,心中充满了对未来的期待,带着满满的收获,继续坚定地前行。

在并发峡谷的尽头,橙序员终于掌握了并发编程的核心技术,他知道,这片峡谷虽然险峻无比,但通过正确的工具和深入的理解,他已经成功跨越了这道难关,向着更高的技术境界迈进。

0

评论区