【多线程进阶】如何保证唱跳rap打篮球的顺序_java会唱跳rap篮球吗 (2)

适用于线程较少的场景,如果线程多了会造成无限套娃,有点麻烦,不够优雅

public class JoinTest {
    // 用来记录啊鸡学习时间
    static double year;

    public static void main(String[] args) {
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
            for (year = 0.5; year <= 5; year += 0.5) {
                System.out.println("开始练习唱跳rap:已练习" + year + "年");
                try {
                    Thread.sleep(288);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //众所周知,练习两年半即可出道
                if (year == 2.5) {
                    System.out.println("===========================>练习时长两年半,出道!!!");
                    //留意下这个break,想想如果不break会怎样
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
            try {
            	// 让threadA线程插队,threadB执行到这儿时会被阻塞,直到threadA执行完
                threadA.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("开始练习打篮球");
        });
        // 启动线程
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述

不管运行多少次,结果都一样,今天就是耶稣来了也一样,我说的

如果不break,那自然是等threadA执行完了threadB才开始执行
在这里插入图片描述

通过volatile

这种实现比较简单,也很好理解,但是性能不咋地,会抢占很多cpu资源,如非必要,不要用

public class VolatileTest {
    //定义一个共享变量用来线程间通信,注意用volatile修饰,保证它内存可见
    static volatile boolean flag = false;
    static double year;

    public static void main(String[] args) {
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
            while (true) {
                if (!flag) {
                    for (year = 0.5; year <= 5; year += 0.5) {
                        System.out.println("开始练习唱跳rap:已练习" + year + "年");
                        try {
                            Thread.sleep(288);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //众所周知,练习两年半即可出道
                        if (year == 2.5) {
                            System.out.println("===========================>练习时长两年半,出道!!!");
                            // 通知threadB你可以执行了
                            flag = true;
                            //同样留意这个break
                            break;
                        }
                    }
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
            while (true) {
            	// 监听flag
                if (flag) {
                    System.out.println("开始练习打篮球");
                    break;
                }
            }
        });
        threadA.start();
        threadB.start();
    }
}

结果与上面第一个一样,就不展示了
关于break,我这里先不说,你先猜猜结果,再复制demo去跑跑,一定要动手

synchronized、wait()、notify()三件套

wait() 和 notify()都是Object类的通讯方法,注意一点,wait和 notify需搭配synchronized使用,注意,notify不会释放锁,至于不会释放锁体现在哪儿,这个demo下面有说明

public class SynchronizedTest {
    static double year;

    public static void main(String[] args) {
        SynchronizedTest sync= new SynchronizedTest();
        sync.execute();
    }

    public void execute() {
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
            synchronized (this) {
                for (year = 0.5; year <= 5; year += 0.5) {
                    try {
                        System.out.println("开始练习唱跳rap:已练习" + year + "年");
                        Thread.sleep(288);
                        if (year == 2.5) {
                            System.out.println("===========================>练习时长两年半,出道!!!");
                            //唤醒等待中的threadB,但threadB不会立马执行,而是等待threadA执行完,因为notify不会释放锁
                            notify();
                            break;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
            synchronized (this) {
                try {
                    wait();
                    System.out.println("开始练习打篮球");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //注意,一定要先启动B,不然会导致B永远阻塞
        threadB.start();
        threadA.start();
    }
}

这个threadA里面的break一定要多想想,跑一跑你就知道啥叫不会释放锁
如果没有break,threadA在唤醒threadB后,会继续执行自己的逻辑,等自己执行完了才会释放锁,这时候threadB才开始执行

基于ReentrantLock

ReentrantLock是juc包下的并发工具,也能实现,但相对复杂,需结合Condition的await和signal,底层原理有点像上面的wait和notify

这里留个课后作业:思考一下为什么unlock要放在finally里面?

public class ReentrantLockTest {
    static double year;

    public static void main(String[] args) {
    	//实例化一个锁和Condition
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
            lock.lock();
            try {
                for (year = 0.5; year <= 5; year += 0.5) {
                    System.out.println("开始练习唱跳rap:已练习" + year + "年");
                    Thread.sleep(288);
                    //众所周知,练习两年半即可出道
                    if (year == 2.5) {
                        System.out.println("===========================>练习时长两年半,出道!!!");
                        //唤醒等待中的线程
                        condition.signal();
                        //这里的break也是个彩蛋,去掉它触发隐藏关卡
                        break;
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //解锁
                lock.unlock();
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
            lock.lock();
            try {
                //让当前线程等待
                condition.await();
                System.out.println("开始练习打篮球");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        //必须保证B先拿到锁,不然会导致A永远阻塞
        threadB.start();
        threadA.start();
    }
}

基于CountDownLatch

这也是juc包下的并发工具,主要有两个常用方法,countDown和await

最后

分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。

面试经验技巧篇
  • 经验技巧1 如何巧妙地回答面试官的问题
  • 经验技巧2 如何回答技术性的问题
  • 经验技巧3 如何回答非技术性问题
  • 经验技巧4 如何回答快速估算类问题
  • 经验技巧5 如何回答算法设计问题
  • 经验技巧6 如何回答系统设计题
  • 经验技巧7 如何解决求职中的时间冲突问题
  • 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
  • 经验技巧9 在被企业拒绝后是否可以再申请
  • 经验技巧10 如何应对自己不会回答的问题
  • 经验技巧11 如何应对面试官的“激将法”语言
  • 经验技巧12 如何处理与面试官持不同观点这个问题
  • 经验技巧13 什么是职场暗语

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

资料整理不易,点个关注再走吧

网金融企业软件工程师笔试题

  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

[外链图片转存中…(img-GeM03IQU-1714524626485)]

资料整理不易,点个关注再走吧

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个非常好的编程问题。这里是一个 Java 多线程的全场篮球比赛的例子,打满四节比赛结束的实现: ```java import java.util.concurrent.ThreadLocalRandom; public class BasketballGame implements Runnable { private final String team1; private final String team2; private final int numMaxPlayers; private final int numQuarters; private final int numThreads; private int team1Score = 0; private int team2Score = 0; private int currentQuarter = 0; public BasketballGame(String team1, String team2, int numMaxPlayers, int numQuarters, int numThreads) { this.team1 = team1; this.team2 = team2; this.numMaxPlayers = numMaxPlayers; this.numQuarters = numQuarters; this.numThreads = numThreads; } @Override public void run() { while (currentQuarter < 4) { synchronized (this) { while (currentQuarter < 4) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } } System.out.println("Quarter " + currentQuarter + " is over.\n" + team1 + " scored " + team1Score + " points.\n" + team2 + " scored " + team2Score + " points.\n"); } } public void play() throws InterruptedException { Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { threads[i] = new Thread(this); threads[i].start(); } for (int quarter = 0; quarter < numQuarters; quarter++) { System.out.println("Quarter " + (quarter + 1) + " started!"); currentQuarter = quarter; for (int i = 0; i < numMaxPlayers; i++) { ThreadLocalRandom random = ThreadLocalRandom.current(); boolean team1Possession = random.nextBoolean(); int points = random.nextInt(1, 3); if (team1Possession) { team1Score += points; } else { team2Score += points; } System.out.println("Player " + (i + 1) + " from " + (team1Possession ? team1 : team2) + " scored " + points + " points.\n" + team1 + " has " + team1Score + " points.\n" + team2 + " has " + team2Score + " points.\n"); } synchronized (this) { currentQuarter++; notifyAll(); } } for (Thread thread : threads) { thread.join(); } System.out.println("Game over!\n" + team1 + " scored " + team1Score + " points.\n" + team2 + " scored " + team2Score + " points.\n"); } public static void main(String[] args) throws InterruptedException { BasketballGame game = new BasketballGame("Lakers", "Warriors", 5, 4, 2); game.play(); } } ``` 这个程序创建 `numThreads` 个线程,每个线程运行一个 `BasketballGame` 实例。然后,在每个季节开始时,每个球员从两个队伍中随机选择一个,然后根据一定的概率进行得分。每个线程等待所有线程完成当前季度的比赛,然后输出比分。最后清空比分并开始下一个季度在新的独立线程中进行比赛,比赛运行完毕时结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值