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

最后

面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典

  • Java核心知识整理

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

Java核心知识

  • Spring全家桶(实战系列)

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

  • 其他电子书资料

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

Step3:刷题

既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。

以下是我私藏的面试题库:

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

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

需要这份系统化的资料的朋友,可以点击这里获取

通过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
简单说下原理:CountDownLatch底层维护了一个计数器count,在实例化的时候设置,当调用countDown方法时,count减一,如果count在减一前已经为0,那么什么都不会发生,如果减一后变成0,则唤醒所有等待的线程;await方法会使当前线程等待,直到count为0

public class CountDownLatchTest {
    static double year;

    public static void main(String[] args) {
    	//实例化一个CountDownLatch,count设置为1,也就是说,只要调用一次countDown方法就会唤醒线程
        CountDownLatch latch = new CountDownLatch(1);
        //线程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("===========================>练习时长两年半,出道!!!");
                    //计数器减一
                    latch.countDown();
                    //老规矩,去掉break触发隐藏关卡
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
            try {
                //阻塞当前线程,计数器为0时被唤醒
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("开始练习打篮球");
        });
        threadA.start();
        threadB.start();
    }
}


打完收工

写在最后

还有一份JAVA核心知识点整理(PDF):JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算…

image

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

需要这份系统化的资料的朋友,可以点击这里获取

,YARN,机器学习,云计算…

[外链图片转存中…(img-CfaHoJWB-1715483537975)]

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

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值