开始改变第四天 Java并发(2)

2025-12-12 0 348

并发编程:像管理团队一样管理线程

一、并发 vs 并行 vs 串行:厨房工作模式

1.1 串行:一个人做完所有事

生活比喻:家里只有你一个人做饭,必须按顺序:洗菜切菜 → 炒菜 → 装盘

public class SerialCooking {
    public static void main(String[] args) {
        System.out.println(\"‍ 开始做饭...\");
        
        washVegetables();  // 洗菜 - 3秒
        cutVegetables();   // 切菜 - 2秒  
        cook();           // 炒菜 - 4秒
        serve();          // 装盘 - 1秒
        
        System.out.println(\" 所有任务完成!\");
    }
    
    static void washVegetables() {
        try {
            Thread.sleep(3000);
            System.out.println(\" 菜洗好了\");
        } catch (InterruptedException e) {}
    }
    
    static void cutVegetables() {
        try {
            Thread.sleep(2000);
            System.out.println(\" 菜切好了\");
        } catch (InterruptedException e) {}
    }
    
    static void cook() {
        try {
            Thread.sleep(4000);
            System.out.println(\" 菜炒好了\");
        } catch (InterruptedException e) {}
    }
    
    static void serve() {
        try {
            Thread.sleep(1000);
            System.out.println(\"️  菜装盘了\");
        } catch (InterruptedException e) {}
    }
}

结果:总共10秒,一个人干完所有活

1.2 并发:一个人轮流做多件事

生活比喻:你还是一个人,但边煮饭边准备其他菜,快速切换

public class ConcurrentCooking {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(\"‍ 开始并发做饭...\");
        long startTime = System.currentTimeMillis();
        
        Thread riceThread = new Thread(() -> {
            try {
                System.out.println(\" 开始煮饭\");
                Thread.sleep(5000);
                System.out.println(\" 饭煮好了\");
            } catch (InterruptedException e) {}
        });
        
        Thread soupThread = new Thread(() -> {
            try {
                System.out.println(\" 开始做汤\");
                Thread.sleep(3000);
                System.out.println(\" 汤做好了\");
            } catch (InterruptedException e) {}
        });
        
        Thread dishThread = new Thread(() -> {
            try {
                System.out.println(\" 开始炒菜\");
                Thread.sleep(4000);
                System.out.println(\" 菜炒好了\");
            } catch (InterruptedException e) {}
        });
        
        riceThread.start();
        soupThread.start();
        dishThread.start();
        
        // 等待所有线程完成
        riceThread.join();
        soupThread.join();
        dishThread.join();
        
        long endTime = System.currentTimeMillis();
        System.out.println(\" 并发做饭完成!总时间: \" + (endTime - startTime)/1000 + \"秒\");
    }
}

结果:大约5秒(最慢的任务时间),一个人快速切换任务

1.3 并行:团队协作

生活比喻:专业厨房,厨师、助手、装盘师同时工作

public class ParallelCooking {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(\"‍‍ 开始团队协作做饭...\");
        long startTime = System.currentTimeMillis();
        
        // 创建线程池 - 像组建厨房团队
        ExecutorService kitchenTeam = Executors.newFixedThreadPool(3);
        
        // 分配任务 - 不同的人同时做不同的事
        Future chef = kitchenTeam.submit(() -> {
            System.out.println(\"‍ 大厨开始炒菜\");
            try { Thread.sleep(4000); } catch (InterruptedException e) {}
            System.out.println(\"‍ 大厨:菜炒好了\");
        });
        
        Future assistant = kitchenTeam.submit(() -> {
            System.out.println(\"‍ 助手开始准备配菜\");
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            System.out.println(\"‍ 助手:配菜准备好了\");
        });
        
        Future server = kitchenTeam.submit(() -> {
            System.out.println(\"‍ 装盘师准备餐具\");
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            System.out.println(\"‍ 装盘师:餐具准备好了\");
        });
        
        // 等待所有任务完成
        chef.get();
        assistant.get();
        server.get();
        
        kitchenTeam.shutdown();
        
        long endTime = System.currentTimeMillis();
        System.out.println(\" 团队协作完成!总时间: \" + (endTime - startTime)/1000 + \"秒\");
    }
}

结果:大约4秒(最慢的任务时间),多人真正同时工作

二、并发问题:当多个人操作同一个东西

2.1 竞态条件:双十一抢购

生活比喻:最后一件商品,你和朋友同时点击\”立即购买\”

public class RaceConditionExample {
    private static int inventory = 1; // 库存只有1件
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println(\" 开始抢购,库存: \" + inventory);
        
        Thread you = new Thread(() -> buy(\"你\"));
        Thread friend = new Thread(() -> buy(\"朋友\"));
        
        you.start();
        friend.start();
        
        you.join();
        friend.join();
        
        System.out.println(\" 最终库存: \" + inventory);
    }
    
    static void buy(String buyer) {
        if (inventory > 0) {
            // 模拟网络延迟
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            
            inventory--; // 库存减1
            System.out.println(\" \" + buyer + \" 抢购成功!\");
        } else {
            System.out.println(\" \" + buyer + \" 抢购失败,库存不足\");
        }
    }
}

可能的结果

 开始抢购,库存: 1
 你 抢购成功!
 朋友 抢购成功!
 最终库存: -1

问题:两个人都成功,但库存变负数!

2.2 解决方案:加锁,像超市收银台排队

public class SafeShopping {
    private static int inventory = 1;
    private static final Object lock = new Object(); // 创建一个锁对象
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println(\" 安全抢购开始,库存: \" + inventory);
        
        Thread you = new Thread(() -> safeBuy(\"你\"));
        Thread friend = new Thread(() -> safeBuy(\"朋友\"));
        
        you.start();
        friend.start();
        
        you.join();
        friend.join();
        
        System.out.println(\" 最终库存: \" + inventory);
    }
    
    static void safeBuy(String buyer) {
        synchronized (lock) { // 一次只让一个人进入这个区域
            if (inventory > 0) {
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                
                inventory--;
                System.out.println(\" \" + buyer + \" 抢购成功!\");
            } else {
                System.out.println(\" \" + buyer + \" 抢购失败,库存不足\");
            }
        }
    }
}

结果

 安全抢购开始,库存: 1
 你 抢购成功!
 朋友 抢购失败,库存不足
 最终库存: 0

三、可见性问题:公告栏信息不同步

3.1 问题演示:经理发通知,员工看不到

public class VisibilityProblem {
    private static boolean meetingCancelled = false; // 会议取消标志
    
    public static void main(String[] args) throws InterruptedException {
        // 员工线程:不断检查会议是否取消
        Thread employee = new Thread(() -> {
            System.out.println(\"‍ 员工:等待会议开始...\");
            int checkCount = 0;
            
            while (!meetingCancelled) {
                checkCount++;
                // 员工一直在检查...
            }
            
            System.out.println(\"‍ 员工:收到通知,会议取消!检查了 \" + checkCount + \" 次\");
        });
        
        // 经理线程:取消会议
        Thread manager = new Thread(() -> {
            try {
                Thread.sleep(1000); // 经理思考1秒
                meetingCancelled = true;
                System.out.println(\"‍ 经理:会议已取消!\");
            } catch (InterruptedException e) {}
        });
        
        employee.start();
        manager.start();
        
        employee.join();
        manager.join();
    }
}

可能的结果:员工线程永远看不到会议取消!

3.2 解决方案:用大喇叭广播(volatile)

public class VisibilitySolution {
    private static volatile boolean meetingCancelled = false; // 加volatile
    
    public static void main(String[] args) throws InterruptedException {
        Thread employee = new Thread(() -> {
            System.out.println(\"‍ 员工:等待会议开始...\");
            int checkCount = 0;
            
            while (!meetingCancelled) {
                checkCount++;
            }
            
            System.out.println(\"‍ 员工:收到通知,会议取消!检查了 \" + checkCount + \" 次\");
        });
        
        Thread manager = new Thread(() -> {
            try {
                Thread.sleep(1000);
                meetingCancelled = true;
                System.out.println(\"‍ 经理:会议已取消!\");
            } catch (InterruptedException e) {}
        });
        
        employee.start();
        manager.start();
        
        employee.join();
        manager.join();
    }
}

结果:员工能立即看到经理的通知

四、死锁:十字路口堵车

4.1 死锁产生

public class DeadlockExample {
    private static final Object northSouthRoad = new Object(); // 南北向道路
    private static final Object eastWestRoad = new Object();   // 东西向道路
    
    public static void main(String[] args) {
        // 南北方向的车
        Thread northSouthCar = new Thread(() -> {
            synchronized (northSouthRoad) {
                System.out.println(\" 南北车:占用了南北道路\");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                
                System.out.println(\" 南北车:等待东西道路...\");
                synchronized (eastWestRoad) {
                    System.out.println(\" 南北车:通过十字路口\");
                }
            }
        });
        
        // 东西方向的车  
        Thread eastWestCar = new Thread(() -> {
            synchronized (eastWestRoad) {
                System.out.println(\" 东西车:占用了东西道路\");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                
                System.out.println(\" 东西车:等待南北道路...\");
                synchronized (northSouthRoad) {
                    System.out.println(\" 东西车:通过十字路口\");
                }
            }
        });
        
        northSouthCar.start();
        eastWestCar.start();
    }
}

结果:两辆车都卡住,谁也过不去!

4.2 死锁解决:统一方向规则

public class DeadlockSolution {
    private static final Object roadA = new Object();
    private static final Object roadB = new Object();
    
    public static void main(String[] args) {
        // 规定:必须先申请roadA,再申请roadB
        Thread car1 = new Thread(() -> {
            synchronized (roadA) {
                System.out.println(\" 车1:占用了道路A\");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                
                synchronized (roadB) {
                    System.out.println(\" 车1:通过十字路口\");
                }
            }
        });
        
        Thread car2 = new Thread(() -> {
            synchronized (roadA) {  // 同样先申请roadA
                System.out.println(\" 车2:占用了道路A\");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                
                synchronized (roadB) {
                    System.out.println(\" 车2:通过十字路口\");
                }
            }
        });
        
        car1.start();
        car2.start();
    }
}

五、synchronized 的三种用法

5.1 实例方法同步 – 锁住当前对象

public class BankAccount {
    private int balance = 1000;
    
    // 同步实例方法 - 锁是当前账户对象(this)
    public synchronized void withdraw(String user, int amount) {
        if (balance >= amount) {
            System.out.println(user + \" 开始取款 \" + amount);
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            
            balance -= amount;
            System.out.println(user + \" 取款成功,余额: \" + balance);
        } else {
            System.out.println(user + \" 余额不足\");
        }
    }
}

5.2 静态方法同步 – 锁住整个类

public class Bank {
    private static int totalMoney = 100000;
    
    // 同步静态方法 - 锁是Bank.class
    public static synchronized void audit() {
        System.out.println(\"开始查账...\");
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        System.out.println(\"查账完成,总资金: \" + totalMoney);
    }
}

5.3 同步代码块 – 灵活控制

public class SmartBankAccount {
    private int balance = 1000;
    private final Object lock = new Object(); // 专门的锁对象
    
    public void transfer(String from, String to, int amount) {
        // 非同步操作
        System.out.println(from + \" 向 \" + to + \" 转账 \" + amount);
        
        // 只同步关键部分
        synchronized (lock) {
            if (balance >= amount) {
                try { Thread.sleep(500); } catch (InterruptedException e) {}
                balance -= amount;
                System.out.println(\"转账成功,余额: \" + balance);
            } else {
                System.out.println(\"余额不足,转账失败\");
            }
        }
        
        // 其他非同步操作
        System.out.println(\"转账操作完成\");
    }
}

六、原子类:内置的\”安全操作\”

6.1 原子计数器

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0); // 原子整数
    
    public void safeIncrement() {
        int newValue = count.incrementAndGet(); // 原子自增
        System.out.println(Thread.currentThread().getName() + \" 增加后: \" + newValue);
    }
    
    public static void main(String[] args) throws InterruptedException {
        AtomicCounter counter = new AtomicCounter();
        
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.safeIncrement();
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println(\"最终计数: \" + counter.count.get());
    }
}

七、实战总结:并发编程最佳实践

7.1 简单的计数器对比

public class CounterComparison {
    // 不安全计数器
    private int unsafeCount = 0;
    
    // 安全计数器 - synchronized
    private int safeCount = 0;
    
    // 安全计数器 - 原子类
    private AtomicInteger atomicCount = new AtomicInteger(0);
    
    public static void main(String[] args) throws InterruptedException {
        CounterComparison demo = new CounterComparison();
        
        System.out.println(\" 测试不安全计数器:\");
        demo.testUnsafeCounter();
        
        System.out.println(\"n 测试安全计数器(synchronized):\");
        demo.testSafeCounter();
        
        System.out.println(\"n 测试原子计数器:\");
        demo.testAtomicCounter();
    }
    
    void testUnsafeCounter() throws InterruptedException {
        unsafeCount = 0;
        Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) unsafeCount++; });
        Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) unsafeCount++; });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println(\"预期: 20000, 实际: \" + unsafeCount);
    }
    
    void testSafeCounter() throws InterruptedException {
        safeCount = 0;
        Thread t1 = new Thread(() -> { 
            for (int i = 0; i < 10000; i++) {
                synchronized (this) {
                    safeCount++;
                }
            }
        });
        Thread t2 = new Thread(() -> { 
            for (int i = 0; i < 10000; i++) {
                synchronized (this) {
                    safeCount++;
                }
            }
        });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println(\"预期: 20000, 实际: \" + safeCount);
    }
    
    void testAtomicCounter() throws InterruptedException {
        atomicCount.set(0);
        Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) atomicCount.incrementAndGet(); });
        Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) atomicCount.incrementAndGet(); });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println(\"预期: 20000, 实际: \" + atomicCount.get());
    }
}

八、记住这些要点

  1. 并发就像单人杂技:快速切换,看起来同时
  2. 并行就像团队协作:真正同时工作
  3. 锁就像会议室:一次只进一个人
  4. volatile就像大喇叭:一有变化通知所有人
  5. 原子类就像自动售货机:内置安全机制

黄金法则:先保证正确性,再考虑性能。没有正确的并发,再快的速度也是徒劳!

通过这些生活化的比喻和实际的代码示例,相信你对并发编程有了更直观的理解。记住,并发编程的本质就是:在共享的环境中维持秩序

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 开始改变第四天 Java并发(2) https://www.zuozi.net/35769.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务