首页 > 其他分享 >【多线程面试题】涵盖三个常见的场景

【多线程面试题】涵盖三个常见的场景

时间:2024-09-22 11:49:43浏览次数:9  
标签:面试题 Thread lock 涵盖 private num static new 多线程

1.多线程轮流打印数字

最简单的代码题,使用两种加锁方式,sychronized和ReentranLock

sychronized

public class Main {
    private static final Object lock = new Object();
    private static int num = 100;
    private static int cnt = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    if (num <= 0) {
                        break;
                    }
                    if (num % 2 == 0) {
                        num--;
                        cnt++;
                        System.out.println(Thread.currentThread().getName() + ":打印" + cnt);
                        lock.notify();
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    if (num <= 0) {
                        break;
                    }
                    if (num % 2 == 1) {
                        num--;
                        cnt++;
                        System.out.println(Thread.currentThread().getName() + ":打印" + cnt);
                        lock.notify();
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

ReentranLock

public class Main {
    private static final ReentrantLock lock = new ReentrantLock();
    private static final Condition condition = lock.newCondition();
    private static int num = 100;
    private static int cnt = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    if (num <= 0) {
                        break;
                    }
                    if (num % 2 == 0) {
                        cnt++;
                        num--;
                        System.out.println(Thread.currentThread().getName() + ":打印" + cnt);
                        condition.signal();
                    } else {
                        condition.await();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    if (num <= 0) {
                        break;
                    }
                    if (num % 2 == 1) {
                        cnt++;
                        num--;
                        System.out.println(Thread.currentThread().getName() + ":打印" + cnt);
                        condition.signal();
                    } else {
                        condition.await();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
        });
        t1.start();
        t2.start();
    }

}

多线程竞争数字并统计结果

已经使用过sychronized和ReentranLock,下面使用原子类来完成

public class Main {
    private static final AtomicInteger num = new AtomicInteger(100);

    private static FutureTask<Integer> getTask() {
        return new FutureTask<>(() -> {
            int cnt = 0;
            while (num.get() > 0) {
                num.getAndDecrement();
                cnt++;
            }
            return cnt;
        });
    }

    private static class ThreadMsg {
        @Override
        public String toString() {
            return "ThreadMsg{" +
                    "threadName='" + threadName + '\'' +
                    ", score=" + score +
                    '}';
        }

        public ThreadMsg(String threadName, Integer score) {
            this.threadName = threadName;
            this.score = score;
        }

        private String threadName;
        private Integer score;

        public Integer getScore() {
            return score;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> task1 = getTask();
        FutureTask<Integer> task2 = getTask();
        FutureTask<Integer> task3 = getTask();
        Thread t1 = new Thread(task1);
        Thread t2 = new Thread(task2);
        Thread t3 = new Thread(task3);
        t1.start();
        t2.start();
        t3.start();
        ArrayList<ThreadMsg> list = new ArrayList<ThreadMsg>() {{
            add(new ThreadMsg(t1.getName(), task1.get()));
            add(new ThreadMsg(t2.getName(), task2.get()));
            add(new ThreadMsg(t3.getName(), task3.get()));
        }};
        list.sort(Comparator.comparingInt(ThreadMsg::getScore));
        System.out.println(list);
    }
}

由于非公平竞争,可能会出现一个线程抢完所有所有数字,可以人为引入延迟

Thread.sleep(1)

也可以加大原子类初始数字

以下为使用ReentranLock,其他不变

private static int num = 100;
    private static final ReentrantLock lock = new ReentrantLock();

    private static FutureTask<Integer> getTask() {
        return new FutureTask<>(() -> {
            int cnt = 0;
            while (true) {
                lock.lock();
                try {
                    if (num <= 0) {
                        break;
                    }
                    num--;
                    cnt++;
                } finally {
                    lock.unlock();
                }
            }
            return cnt;
        });
    }

生产者消费者模型

生产者消费者模型

在此处缓冲区使用线程安全类BlockingQueue来实现,其安全性由原子ReentranLock锁实现,其put和take都是阻塞的,所以无需加锁,实现更加简单
put源码

以下为具体实现

public class Main {
    private static final BlockingQueue<Integer> buffer = new LinkedBlockingQueue<>(10);
    private static final Random random = new Random();

    static class Producer implements Runnable {

        @Override
        public void run() {
            try {
                int value = random.nextInt(1000);
                buffer.put(value);
                System.out.println(Thread.currentThread().getName() + ":生产了" + value);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }

    static class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                Integer i = buffer.take();
                System.out.println(Thread.currentThread().getName() + ":消费了" + i);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(new Producer(), "生产者" + i).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(new Consumer(), "消费者者" + i).start();
        }
    }

}

标签:面试题,Thread,lock,涵盖,private,num,static,new,多线程
From: https://blog.csdn.net/weixin_46684043/article/details/142414026

相关文章

  • 云原生k8s高频面试题
    1.k8sservice有哪些类型?通过创建service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。其主要类型有:ClusterIP:虚拟的服务IP地址,该地址用于k8s集群内部的pod访问,在Node上kube-proxy通过设置的iptables规则进行转发;NodeP......
  • Java多线程大全
    文章目录简介多线程使用场景后台任务:多线程的基本概念Java程序是如何运行的?线程的创建和启动1、线程的创建和启动1.1、继承Thread类1.2、实现Runnable接口2、线程的调度与控制2.1、线程优先级2.2、Thread.sleep3、Thread中几个方法、......
  • Android RecyclerView 缓存机制深度解析与面试题
    本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点引言RecyclerView是Android开发中用于展示列表和网格的强大组件。它通过高效的缓存机制,优化了滑动性能和内存使用。本文将深入探讨RecyclerView的缓存机制,并......
  • Java笔试面试题AI答之单元测试JUnit(7)
    文章目录37.请列举一些JUnit扩展?1.参数化测试2.条件测试执行3.临时目录4.时间测试5.重复测试6.前置/后置条件7.Mockito8.SpringTest9.JUnitVintage10.Testcontainers11.自定义注解和扩展12.测试监听器(TestListener和RunListener)38.请列举Java程序员......
  • 50道渗透测试面试题,全懂绝对是高手
    1. 什么是渗透测试,它与安全评估的主要区别是什么? 渗透测试是一种模拟真实攻击的过程,旨在发现并利用系统中的安全漏洞。它侧重于深入探索系统的脆弱性,并尝试获取未授权访问。相比之下,安全评估更广泛地评估系统的安全性,可能包括合规性检查、策略审查等多个方面,而不一定涉......
  • spring常见面试题
    介绍一下springbean的生命周期1.加载配置转化成springbean的定义。2.使用jdk动态代理根据bean的定义创建bean的实例并封装成beanwrapper。3.执行populateBean()属性填充方法。4.执行initializeBean()方法进行bean的初始化,在初始化中,如果Bean实现了BeanNameAware接⼝,调⽤set......
  • 【Kubernetes】常见面试题汇总(二十四)
    目录 71.假设一家公司想要修改它的部署方法,并希望建立一个更具可扩展性和响应性的平台。您如何看待这家公司能够实现这一目标以满足客户需求?72.考虑一家拥有非常分散的系统的跨国公司,期待解决整体代码库问题。您认为公司如何解决他们的问题?特别说明:题目69-113属于【Kube......
  • 【Kubernetes】常见面试题汇总(二十九)
    目录 81.简述你知道的几种CNI网络插件,并详述其工作原理。K8s常用的CNI网络插件(calico&&flannel),简述一下它们的工作原理和区别。特别说明:题目  1-68  属于【Kubernetes】的常规概念题,即“汇总(一)~(二十二)”。题目69-113属于【Kubernetes】的生产应用题。8......
  • 【Kubernetes】常见面试题汇总(二十三)
    目录 69.考虑一家拥有分布式系统的跨国公司,拥有大量数据中心,虚拟机和许多从事各种任务的员工。您认为这样公司如何以与Kubernetes一致的方式管理所有任务?70.考虑一种情况,即公司希望通过维持最低成本来提高其效率和技术运营速度。您认为公司将如何实现这一目标?特别说明:题......
  • 【Kubernetes】常见面试题汇总(三十)
    目录 82.Worker节点宕机,简述Pods驱逐流程。特别说明:题目  1-68  属于【Kubernetes】的常规概念题,即“汇总(一)~(二十二)”。题目69-113属于【Kubernetes】的生产应用题。82.Worker节点宕机,简述Pods驱逐流程。(1)节点宕机的概述:在Kubernetes集群中,当节......