• 多线程问题整理


    1.如何实现一个生产者和消费者模型?(锁、信号量、线程通信、阻塞队列等)

    Java生产者和消费者模型的5种实现方式

    2.如何理解线程的同步和异步、阻塞和非阻塞?

    作者:Yi Lu
    链接:https://www.zhihu.com/question/19732473/answer/20851256
    来源:知乎

    “阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。
    1.同步与异步
    同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
    所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
    换句话说,就是由调用者主动等待这个调用的结果。

    而异步则是相反,调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

    典型的异步编程模型比如Node.js

    举个通俗的例子:
    你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
    而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

    1. 阻塞与非阻塞

      阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

    阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
    非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

    还是上面的例子,
    你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
    在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

    如果是关心阻塞 IO/ 异步 IO, 参考 Unix Network Programming View Book

    还是2014年写的以解释概念为主,主要是同步异步 阻塞和非阻塞会被用在不同层面上,可能会有不准确的地方,并没有针对 阻塞 IO/ 异步 IO 等进行讨论,大家可以后续看看这两个回答:

    怎样理解阻塞非阻塞与同步异步的区别?

    怎样理解阻塞非阻塞与同步异步的区别?

    3.线程池处理任务的流程是怎样的?

    1,首先线程池判断基本线程池是否已满(< corePoolSize ?)?没满,创建一个工作线程来执行任务。满了,则进入下个流程。

    2,其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。

    3,最后线程池判断整个线程池是否已满(< maximumPoolSize ?)?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

    总结:线程池优先要创建出基本线程池大小(corePoolSize)的线程数量,没有达到这个数量时,每次提交新任务都会直接创建一个新线程,当达到了基本线程数量后,又有新任务到达,优先放入等待队列,如果队列满了,才去创建新的线程(不能超过线程池的最大数maxmumPoolSize)

    4.wait和sleep有什么不同?

    区别1:使用限制

    使用 sleep 方法可以让让当前线程休眠,时间一到当前线程继续往下执行,在任何地方都能使用,但需要捕获 InterruptedException 异常。

    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    

    而使用 wait 方法则必须放在 synchronized 块里面,同样需要捕获 InterruptedException 异常,并且需要获取对象的锁。

    synchronized (lock){
        try {
            lock.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

    而且 wait 还需要额外的方法 notify/ notifyAll 进行唤醒,它们同样需要放在 synchronized 块里面,且获取对象的锁。。

    synchronized (lock) {
        // 随机唤醒
        lock.notify();
    
        // 唤醒全部
        lock.notifyAll();
    }
    

    当然也可以使用带时间的 wait(long millis) 方法,时间一到,无需其他线程唤醒,也会重新竞争获取对象的锁继续执行。

    区别2:使用场景

    sleep 一般用于当前线程休眠,或者轮循暂停操作,wait 则多用于多线程之间的通信。

    区别3:所属类

    sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。

    java.lang.Thread#sleep

    public static native void sleep(long millis) throws InterruptedException;
    

    java.lang.Object#wait

    public final native void wait(long timeout) throws InterruptedException;
    

    为什么要这样设计呢?

    因为 sleep 是让当前线程休眠,不涉及到对象类,也不需要获得对象的锁,所以是线程类的方法。wait 是让获得对象锁的线程实现等待,前提是要楚获得对象的锁,所以是类的方法。

    区别4:释放锁

    Object lock = new Object();
    synchronized (lock) {
        try {
            lock.wait(3000L);
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

    如上代码所示,wait 可以释放当前线程对 lock 对象锁的持有,而 sleep 则不会。

    区别5:线程切换

    sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait 后可能还是有机会重新竞争到锁继续执行的。

    5.Synchronized和ReentrantLock有什么不同?各适合什么场景?

    1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了锁投票定时锁等候中断锁等候
    example:线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
    ReentrantLock获取锁定与三种方式:
    • lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
    • tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
    • tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
    • lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

    2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

    3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;

    作者:OverLight
    链接:https://www.jianshu.com/p/4dbacf1cadcf
    来源:简书

    6.读写锁适用于什么场景?ReentrantReadWriteLock是如何实现的?

    ReentrantReadWriteLock读写锁详解

    7.线程之间如何通信?

    [JAVA多线程之线程间的通信方式](https://www.cnblogs.com/hapjin/p/5492619.html)

    8.保证线程安全的方法有哪些?

    如何保证线程安全

    9.如何尽可能提高多线程并发性能?

    如何提高Java并行程序性能

    10.ThreadLocal用来解决什么问题?ThreadLocal是如何实现的?

    ThreadLocal

    11.死锁的产生条件?如何分析是否有线程死锁?

    Java 多线程死锁的产生以及如何避免死锁

    12.在实际工作中遇到过什么样的并发问题,如何发现(排查)并解决的?

    [Java面试 32个核心必考点完全解析

  • 相关阅读:
    微服务架构技术栈选型手册(万字长文)
    Visual Studio 2013 always switches source control plugin to Git and disconnect TFS
    Visual Studio 2013 always switches source control plugin to Git and disconnect TFS
    MFC对话框中使用CHtmlEditCtrl
    ATL开发 ActiveX控件的 inf文件模板
    ActiveX: 如何用.inf和.ocx文件生成cab文件
    Xslt 1.0中使用Array
    如何分隔两个base64字符串?
    An attempt was made to load a program with an incorrect format
    JQuery 公网 CDN
  • 原文地址:https://www.cnblogs.com/zhaoran8775/p/12839660.html
Copyright © 2020-2023  润新知