zoukankan      html  css  js  c++  java
  • 多线程问题整理

    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个核心必考点完全解析

  • 相关阅读:
    1.1、MyEclipse自定义注释
    angular2 组件内容嵌入(ng-content)
    常用css初始化样式(淘宝)
    web移动端rem的适配
    PSCC2019常用基础操作
    vs Code打开新的文件会覆盖窗口中的文件?
    关于将ECharts引入到项目中的几种方式
    VS code 设置侧边栏字体大小
    Visual Studio Code(VS code)你们都在用吗?或许你们需要看一下这篇博文
    Angular 监听滚动条事件
  • 原文地址:https://www.cnblogs.com/zhaoran8775/p/12839660.html
Copyright © 2011-2022 走看看