精华内容
下载资源
问答
  • 并发编程篇:java 高并发面试题

    万次阅读 多人点赞 2018-02-28 21:43:18
    8、Synchronized 与Lock ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定, 如果...

    1、线程与进程

    1. 进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
    2. 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
    3. 区别不同
      a,地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
      b,资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资
      c,线程是处理器调度的基本单位,但进程不是.
      d,二者均可并发执行.

    2、 守护线程

    在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。
    守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

    3、java thread状态

    1. NEW 状态是指线程刚创建, 尚未启动
    2. RUNNABLE Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得cpu 时间片后变为运行中状态(running)。
    3. BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 也就是这里是线程在等待进入临界区
    4. WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
    5. TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态
    6. TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

    4、请说出与线程同步以及线程调度相关的方法。

    1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
    2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
    3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
    4. notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

    5、进程调度算法

    实时系统:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。
    交互式系统:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

    6、wait()和sleep()的区别

    1. sleep来自Thread类,和wait来自Object类
    2. 调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
    3. sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
    4. sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒

    7、ThreadLocal,以及死锁分析

    hreadLocal为每个线程维护一个本地变量。
    采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
    ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
    ThreadLocal 内存溢出代码演示和原因分析!

    8、Synchronized 与Lock

    1. ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
      线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
      如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
      如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

    2. ReentrantLock获取锁定与三种方式:
      a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
      b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
      c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
      d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

    总体的结论先摆出来:

    synchronized:
    在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
    ReentrantLock:
    ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

    9、Volatile和Synchronized

    Volatile和Synchronized四个不同点:

    1. 粒度不同,前者针对变量 ,后者锁对象和类
    2. syn阻塞,volatile线程不阻塞
    3. syn保证三大特性,volatile不保证原子性
    4. syn编译器优化,volatile不优化
      要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
    5. 对变量的写操作不依赖于当前值。
    6. 该变量没有包含在具有其他变量的不变式中。

    JAVA多线程之volatile 与 synchronized 的比较

    10、CAS

    CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

    11、Java中Unsafe类详解

    1. 通过Unsafe类可以分配内存,可以释放内存;类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。
    2. 可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
    3. 挂起与恢复:将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
    4. cas
      Java中Unsafe类详解

    12、线程池

    线程池的作用:
    在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性。
    常用线程池:ExecutorService 是主要的实现类,其中常用的有
    Executors.newSingleT
    hreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

    13、ThreadPoolExecutor

    ###构造方法参数说明
    corePoolSize:核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
    maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。
    keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收。
    unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。
    workQueue:线程池中的任务队列.
    常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。

    threadFactory:线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

    原理

    1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
    2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
    3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
    4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
    5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。

    14、Executor拒绝策略

    1. AbortPolicy:为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try
      catch,否则程序会直接退出.
    2. DiscardPolicy:直接抛弃,任务不执行,空方法
    3. DiscardOldestPolicy:从队列里面抛弃head的一个任务,并再次execute 此task。
    4. CallerRunsPolicy:在调用execute的线程里面执行此command,会阻塞入
    5. 用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式

    15、CachedThreadPool 、 FixedThreadPool、SingleThreadPool

    1. newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
      适用场景:任务少 ,并且不需要并发执行
    2. newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
      线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。
      适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)
    3. newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    4. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

    16、CopyOnWriteArrayList

    CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。
    使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

    17、AQS

    1. AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
    private volatile int state;//共享变量,使用volatile修饰保证线程可见性
    
    1. 2种同步方式:独占式,共享式。独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock
    2. 节点的状态
      CANCELLED,值为1,表示当前的线程被取消;
      SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
      CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
      PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
      值为0,表示当前节点在sync队列中,等待着获取锁。
    3. 模板方法模式
       protected boolean tryAcquire(int arg) : 独占式获取同步状态,试着获取,成功返回true,反之为false
       protected boolean tryRelease(int arg) :独占式释放同步状态,等待中的其他线程此时将有机会获取到同步状态;
       protected int tryAcquireShared(int arg) :共享式获取同步状态,返回值大于等于0,代表获取成功;反之获取失败;
       protected boolean tryReleaseShared(int arg) :共享式释放同步状态,成功为true,失败为false
      AQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。双端双向链表。
    4. 独占式:乐观的并发策略
      acquire
       a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节;
      b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全;
       c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。
      release
      release的同步状态相对简单,需要找到头结点的后继结点进行唤醒,若后继结点为空或处于CANCEL状态,从后向前遍历找寻一个正常的结点,唤醒其对应线程。
    5. 共享式:
      共享式地获取同步状态.同步状态的方法tryAcquireShared返回值为int。
      a.当返回值大于0时,表示获取同步状态成功,同时还有剩余同步状态可供其他线程获取;
       b.当返回值等于0时,表示获取同步状态成功,但没有可用同步状态了;
       c.当返回值小于0时,表示获取同步状态失败。
    6. AQS实现公平锁和非公平锁
      非公平锁中,那些尝试获取锁且尚未进入等待队列的线程会和等待队列head结点的线程发生竞争。公平锁中,在获取锁时,增加了isFirst(current)判断,当且仅当,等待队列为空或当前线程是等待队列的头结点时,才可尝试获取锁。
       Java并发包基石-AQS详解

    18、Java里的阻塞队列

    7个阻塞队列。分别是

    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    SynchronousQueue:一个不存储元素的阻塞队列。
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

    添加元素

    Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。
    add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
    offer:添加元素到队列里,添加成功返回true,添加失败返回false
    put:添加元素到队列里,如果容量满了会阻塞直到容量不满

    删除方法

    3个删除方法
    poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。
    remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false
    take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除

    19、condition

    对Condition的源码理解,主要就是理解等待队列,等待队列可以类比同步队列,而且等待队列比同步队列要简单,因为等待队列是单向队列,同步队列是双向队列。

    java condition使用及分

    20、DelayQueue

    队列中每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列。

    并发队列-无界阻塞延迟队列delayqueue原理探究

    21、Fork/Join框架

    Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。Fork/Join框架要完成两件事情:

    1.任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割

    2.执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。

    在Java的Fork/Join框架中,使用两个类完成上述操作

    1.ForkJoinTask:我们要使用Fork/Join框架,首先需要创建一个ForkJoin任务。该类提供了在任务中执行fork和join的机制。通常情况下我们不需要直接集成ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了两个子类:

    a.RecursiveAction:用于没有返回结果的任务

    b.RecursiveTask:用于有返回结果的任务

    2.ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行

    任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务(工作窃取算法)。
    Fork/Join框架的实现原理
      ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责将存放程序提交给ForkJoinPool,而ForkJoinWorkerThread负责执行这

    21、原子操作类

    在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。

    1. 原子更新基本类型
      使用原子方式更新基本类型,共包括3个类:
      AtomicBoolean:原子更新布尔变量
      AtomicInteger:原子更新整型变量
      AtomicLong:原子更新长整型变量
    2. 原子更新数组
      通过原子更新数组里的某个元素,共有3个类:
      AtomicIntegerArray:原子更新整型数组的某个元素
      AtomicLongArray:原子更新长整型数组的某个元素
      AtomicReferenceArray:原子更新引用类型数组的某个元素
      AtomicIntegerArray常用的方法有:
      int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
      boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值
    3. 原子更新引用类型
      AtomicReference:原子更新引用类型
      AtomicReferenceFieldUpdater:原子更新引用类型里的字段
      AtomicMarkableReference:原子更新带有标记位的引用类型。
    4. 原子更新字段类
      如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:
      AtomicIntegerFieldUpdater:原子更新整型字段
      AtomicLongFieldUpdater:原子更新长整型字段
      AtomicStampedReference:原子更新带有版本号的引用类型。
      要想原子更新字段,需要两个步骤:
      每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
      更新类的字段(属性)必须为public volatile

    22、同步屏障CyclicBarrier

    CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

    23、** CyclicBarrier和CountDownLatch的区别**

    CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
    CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

    24、Semaphore

    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源
    Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控,代码如下:

    控制并发线程数的Semaphore

    25、死锁,以及解决死锁

    死锁产生的四个必要条件

    互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
    不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
    请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
    循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

    解决死锁

    一是死锁预防,就是不让上面的四个条件同时成立。
    二是,合理分配资源。
    三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。

    26、进程间的通信方式

    1. 管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    3. 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    4. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    5. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
    6. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
    7. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

    27、中断

    interrupt()的作用是中断本线程。
    本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
    如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
    如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
    如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
    中断一个“已终止的线程”不会产生任何操作。

    1. 终止处于“阻塞状态”的线程
      通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
      当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程,
    2. 终止处于“运行状态”的线程

    28、interrupted() 和 isInterrupted()的区别

    最后谈谈 interrupted() 和 isInterrupted()。
    interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
    区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。
    interrupt()和线程终止方式

    展开全文
  • 高并发常见的面试题

    万次阅读 多人点赞 2019-02-28 23:45:06
    为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量、方法参数和栈调用,一个线程中存储的变量对其他线程是不可见的。而堆是所有线程...

    1. 什么是进程
    进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间)。
    比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左边的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。

    2. 什么是线程
    进程是表示自愿分配的基本单位。而线程则是进程中执行运算的最小单位,即执行处理机调度的基本单位。通俗来讲:一个程序有一个进程,而一个进程可以有多个线程。

    3. 多线程的几种实现方式
    (1) 继承Thread类创建线程

    Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法将启动一个新线程,并执行run()方法。这种方式实现多线程比较简单,通过自己的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。
    (2) 实现Runnable接口创建线程

    如果自己的类已经继承了两一个类,就无法再继承Thread,因此可以实现一个Runnable接口
    (3) 实现Callable接口通过FutureTask包装器来创建Thread线程

    (4) 使用ExecutorService、Callable、Future实现有返回结果的线程

    ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。
    可返回值的任务必须实现Callable接口;无返回值的任务必须实现Runnabel接口。
    执行Callable任务后,可以获取一个Future对象,在该对象上调用get()方法就可以获取到Callable任务返回的Object了。(get()方法是阻塞的,线程无返回结果,该方法就一直等待)
    4. 什么是线程局部变量*
    ThreadLocal并非是一个线程本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更合适。线程局部变量(ThreadLocal)功能非常简单,就是为每一个使用该变量的线程都提供了一个变量值副本,是java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。

    5. 进程间如何通讯
    管道(pipe)

    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系
    有名管道(namedpipe)

    有名管道也是半双工的通信方式,但是它云溪无亲缘关系进程间的通信。
    信号量(semaphore)

    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    消息队列(messagequeue)

    消息队列里有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递消息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
    信号(signal)

    信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生
    共享内存(shared memory)

    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
    套接字(socket)

    套接口也是一种进程间通信机制,以其他通信机制不同的是,它可用于不同进程间的通信
    6. 线程间如何通讯
    锁机制:包括互斥锁、条件变量、读写锁

    互斥锁提供了以排他方式防止数据结构被并发修改的方法
    读写锁允许多个线程同时读共享数据,而对写操作是互斥的
    条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
    信号量机制:包括无名线程信号量和命名线程信号量

    信号机制:类似进程间的信号处理
    线程间的通信目的只要是用于新城同步,所以线程没有像进程通信中的用于数据交换的通信机制。

    7. 同步和异步有何不同,在什么情况下分别使用它们?举例说明
    如果数据将在线程间共享。例如:正在写的数据以后可能会被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取
    当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效。
    同步交互:指发送一个请求,需要等待返回,然后才能发送下一个请求,有个等待的过程
    异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
    区别:一个需要等待,一个不需要等待

    8. ConcurrentHashMap 和 Hashtable的区别
    它们都可以用于多线程的环境,但当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。

    HashTable的任何操作都会把整个表锁住,是阻塞的。好处是:总能获取最实时的更新,比如说线程A调用putAll()写入大量数据,期间线程B调用get(),线程B就会被阻塞,直到线程A完成putAll(),因此线程B肯定能获取到线程A写入的完整数据。坏处是所有调用都需要排队,效率较低。
    ConcurrentHashMap是设计为非阻塞的。在更新时会局部锁住某部分数据,但不会把整个表都锁住。同步读取操作则是完全非阻塞的。好处是在保证合理的同步前提下,效率很高。坏处是:严格来说,读取操作不能保证反映最近的更新。例如线程A调用putAll()写入大量数据,期间线程B调用get(),则只能get()到目前为止已经顺利插入的部分数据。
    JDK8的版本,与JDK6的版本有很大差异。实现线程安全的思想也已经完全变了,它摒弃了Segment(分段锁)的概念,而是启用了一种全新的方式实现,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由数组+链表+红黑树的方式思想,但是为了做到并发,又增加了很多复制类,例如TreeBin、Traverser等对象内部类。CAS算法实现无锁化的修改至操作,他可以大大降低锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。
    9. Hashtable与Hashmap的区别
    HashTable与Hashmap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。
    在HashMap中,null可以作为键,这样的键只能有一个;可以有一个或者多个键所对应的值为null;当get()方法返回null时,既可以表示Hashmap中没有该键,也可以表示该键锁对应的值为null。因此,在Hashmap中不能由get()方法来判断HashMap中是否存在某个键,而应该使用containsKey()方法来判断。
    在HashTable中,无论键key还是值value都不能为null
    这两个 类最大的不同之处在于:
    HashTable是线程安全的,它的方法是同步的,可以直接用于多线程环境中
    HashMap是线程不安全的,在多线程环境中,需要手动实现同步机制
    10. ArrayBlockingQueue的用法
    一个线程向一个固定大小的队列里面不停地存放数据,另一个线程不停地向这个队列里面取数据,当队列满了,还继续存放数据,此时出现阻塞,直到队列有空闲的位置;反之,当队列为空,还继续取数据,则也出现阻塞,直到队列中有数据为止

    11. 线程和进程有什么区别
    线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。

    12. 用Runnable还是用Thread
    大家知道我们可以通过继承Thread类或者调用Runnable接口来实现线程,问题是,哪个方法更好呢?什么情况下使用哪种呢?如果你要继承其他的类,就实现Runnable接口

    13. Thread类中的strat()和run()方法有什么区别?
    start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动,而调用start()方法会启动一个新的线程。

    14. java中Runnable和Callable的区别
    Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。
    他们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。
    15. 什么是java内存模型
    java内存模型定义了java虚拟机在计算机内存中的工作方式。JMM决定了一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每一个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

    16. 什么是线程安全
    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果都是一样的,而且其他变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。

    17. java中如何停止一个线程
    java提供了很丰富的API但没有为停止线程提供API。JDK1.0本来有一些像stop()、suspend()和resume()的控制方法但是由于潜在的死锁威胁,因此在后续的JDK版本中他们被摒弃了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run()或者call()方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以使用volatile布尔变量来推出run()方法的循环或者是取消任务来中断线程

    18. 一个线程运行时发生异常会怎样?
    如果异常没有被捕获该线程将会停止执行。
    Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌借口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理**
    19. 如何在两个线程间共享数据?
    可以通过共享对象来实现这个目的,或者是使用阻塞队列,或者使用wait()和notify()方法**

    20. java中的notify和notifyAll有什么区别?
    如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
    当有线程调用了对象的notifyAll()方法(唤醒所有wait线程)或者notify()方法(只随机唤醒一个wait线程),被唤醒的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify方法后只要一个线程会由等待池进入锁池,而notifyAll方法会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
    优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
    notify可能会产生死锁
    21. 为什么wait、notify和notifyAll这些方法不再thread类里面
    这是个设计相关的问题,它考察的是面试者对现有系统和一些普遍存在但看起来不合理的事务的看法。回答这类问题的时候,你要说明为什么把这些方法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是java提供的锁时对象级的而不是线程级的,每个对象都有所,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在thread类中,线程正在等待的是哪个锁就不明显了。

    22. 什么是FutureTask?
    在java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能返回,如果运算尚未完成,get()方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnbale接口所以它可以提交给Executor来执行

    23. 为什么wait和notify方法要在同步快中调用?
    主要是因为java API强制要求这样做,如果你不这样做,你的代码会抛出IllegalMonitorStateException异常。
    为了避免wait和notify之间产生竞态条件

    24. java中堆和栈有什么不同?
    为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量、方法参数和栈调用,一个线程中存储的变量对其他线程是不可见的。而堆是所有线程共享的一片公共内存区域。对象都在堆里创建,为了提升效率,线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile变量就可以发挥作用了,它要求线程从主存中读取变量的值

    25. 什么是线程池?为什么要使用它?
    创建线程需要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变成,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,他们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,java API提供了Executor框架让你可以创建不同的线程池。比如单线程吃,数目固定的线程池等

    26. 如何避免死锁?
    java多线程中的死锁:是指两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因此死锁会让你的程序挂起无法完成任务,死锁的发生必须满足一下四个条件

    互斥条件:一个资源每次只能被一个进程使用
    请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
    循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
    避免死锁最简单的方法就是阻塞循环等待条件,将系统中所有资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或者降序)做操作来避免死锁

    27. java中synchronized和ReentrantLock有什么不同?
    java在过去很长一段时间只能通过synchronized关键字来实现互斥,它有一些缺点。比如你不能扩展锁以外的方法或者块边界,尝试获取锁时不能中途取消等。java5 通过Lock接口提供了更复杂的控制来解决这些问题。ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义且它还具有可扩展性。

    28. 有三个线程T1、T2和T3,怎么确保它们按照顺序执行?
    在多线程中有多重方法让线程按特定的顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成

    29. Thread类中的yield方法有什么作用?
    yield方法可以暂停当前正在执行的线程对象,让其他有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证使其他线程一定能占用CPU,执行yield的线程有可能在进入到暂停状态后马上又被执行

    30. java中ConcurrentHashMap的并发度是什么?
    ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。

    31. java线程池中submit()和execute()方法有什么区别?
    两个方法都可以向线程池提交任务,execute()方法的返回类型时void,它定义在Executor接口中,而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口。

    32. volatile变量和atomic变量由什么不同?
    首先volatile变量和atomic变量看起来很像,但是功能却不一样。

    volatile变量可以确保线性关系,即写操作会发生在后续的读操作之前,但它不能保证原子性。例如用volatile修饰count变量,那么count++操作就不是原子性的。
    AtomicInteger类提供的atomic方法可以让这种操作具有原子性,如:getAndIncrement()方法会原子性的进行增量操作把当前值加1.,其他数据类型和引用变量也可以进行相似操作。

    33. 有哪些不同的线程生命周期
    当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建、就绪、运行、阻塞和死亡5种状态。尤其是当线程启动以后,它不可能一直“霸占”着CPU肚子运行,所以CPU需要在多调线程之间切换,于是线程状态也会多次在运行和阻塞之间切换

    展开全文
  • 高并发解决方案相关面试题

    万次阅读 多人点赞 2019-09-06 20:48:16
    占用内存少,并发量强,支持多种并发连接,效率. 2.能够作为负载均衡服务器和(内部直接支持 Rails 和 PHP)代理服务器。Nginx用C编写开销和CPU占有小. 3.安装启动简单,配置简洁,bug少,一般几个月不需要重新启动...

    什么是DNS解析域名

    DNS域名解析就是讲域名转化为不需要显示端口(二级域名的端口一般为80)的IP地址,域名解析的一般先去本地环境的host文件读取配置,解析成对应的IP地址,根据IP地址访问对应的服务器。若host文件未配置,则会去网络运营商获取对应的IP地址和域名.

    什么是Nginx

    Nginx是一个高级的轻量级的web服务器,由俄罗斯科学家开发的,具有如下优点:

         1.占用内存少,并发量强,支持多种并发连接,效率高.

         2.能够作为负载均衡服务器和(内部直接支持 Rails 和 PHP)代理服务器。Nginx用C编写开销和CPU占有小.

         3.安装启动简单,配置简洁,bug少,一般几个月不需要重新启动且不会宕机,稳定性和安全性好.

    Nginx的作用

    反向代理、负载均衡、配置主备tomcat、动静分离

     

    Nginx 应用场景

    做HTTP服务器、反向代理服务器、静态资源服务器

     

    什么是反向代理

    代替真实服务器接收网络请求,然后将请求转发到真实服务器

     

    反向代理的作用

    隐藏真实服务器,使真实服务器只能通过内网访问,保护了真实服务器不被攻击。配置负载均衡,减轻单台真实服务器的压力。配置主备服务器,保持服务稳定运行。

     

    Nginx如何配置反向代理

    首先到DNS服务器做域名解析,如果是局域网在hosts文件中配置IP和域名对应关系。编辑nginx的nginx.conf文件,配置server_name为指向nginx服务器的域名,location拦截请求,如果是访问nginx本地资源则配置root,如果是反向代理到真实服务器则配置proxy_pass为服务器地址

     

    说说常用Nginx的相关配置

    upstream 负载均衡配置

    server [IP] [weight] [backup] 配置tomcat集群

    proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout 连接时间、真实服务器响应时间、返回结果时间

    location 匹配用户请求的url

    root 配置本地资源路径

    proxy_pass    配置真实服务器地址

     

    请画图展示反向代理流程

     

    LVS与Nginx区别

    LVS是四层反向代理,基于TCP和UDP协议,可用于管理Nginx集群,抗负载能力强。Nginx是七层反向代理,基于HTTP协议,用于管理真实服务器集群。

     

    location的作用

    匹配用户请求url,根据不同请求转发到不同的服务器。

     

    Nginx中如何配置负载均衡

    在upstream中配置多个server,在location的proxy_pass配置为http://+upstream名称

     

    四层负载均衡与七层负载均衡区别

    四层负载均衡基于TCP和UDP协议,通过IP+端口号接受请求并转发到服务器。七层负载均衡基于HTTP协议,通过url或主机名接收请求并转发到服务器。

     

    四层负载均衡有那些实现方案

    LVS、F5

     

    负载均衡有那些算法

    轮询算法:按照时间顺序分配到不同的服务器,当其中一台服务器宕机则被自动剔除,切换到正常的服务器。

    权重算法:按照分配给服务器的权重比例来分发到不同服务器,权重比例越高,则访问几率越大。

    IP绑定(ip_hash):根据访问的IP的哈希结果来判定,使同一个IP访问一台固定的后端服务器,同时解决动态页面的session问题.

     

    服务器分布式后,会产生了那些问题

    分布式锁

    分布式全局ID

    分布式Session一致性问题

    分布式事务

    分布式任务调度

    分布式日志收集

    分布式配置中心

     

    什么是动态负载均衡

    一般情况下,使用nginx搭建服务器集群,每次修改nginx.conf配置文件都需要重启nginx服务器。动态负载均衡就是修改nginx.conf配置文件后不必重启nginx而使配置生效。

     

    Nginx如何实现动态负载均衡

    搭建Nginx+Consul+Upsycn环境。Nginx实现服务的反向代理和负载均衡。Consul是一个开源的注册中心和服务发现的框架,通过HTTP API来发现服务,注册服务。同时支持故障发现,K/V存储,多数据中心,Raft算法等多种高可用特性。Consul在Nginx动态负载均衡作用是通过Http api注册和发现服务.Upsycn是新浪微博的开源框架,在Nginx动态负载均衡的作用是Consul的后端的server列表,即获取Nginx的上游服务器(Upstream server)信息,并动态更新Nginx的路由信息.

     

    什么是Http协议

    超文本传输协议

    Http协议组成部分

    Http协议是基于TCP协议封装成超文本传输协议,包括请求(request)和响应(response),http协议请求(request)分为请求参数(request params)和方法类型(request method)、请求头(request hearder)、请求体(request body) ,

    响应(response)分为 响应状态(response state)、响应头(response header)、响应体(response body)等.

     

    TCP与UDP区别

    udp:

       a、是面向无连接, 将数据及源的封装成数据包中,不需要建立连接

        b、每个数据报的大小在限制64k内

        c、因无连接,是不可靠协议

        d、不需要建立连接,速度快

    tcp:

       a、建议连接,形成传输数据的通道.

        b、在连接中进行大数据量传输,以字节流方式

        c 通过三次握手完成连接,是可靠协议

        d 必须建立连接m效率会稍低

     

    谈谈七层网络模型

    应用层:客户端的各种应用、app;

    表示层:进行数据的格式区分,如图片、编码;

    会话层:本地主机与远程主机的会话管理;

    传输层:定义传输数据的协议端口号,TCP和UDP是这一层的协议;

    网络层:进行逻辑地址寻址;

    数据链路层:建立逻辑连接,进行硬件地址寻址;

    物理层:建立物理连接;

     

    Nginx如何实现TCP四层负载均衡

    在nginx.conf文件中配置tcp模块,在upstream块中定义socket服务器负载均衡,其余与nginx配置七层负载均衡相同。

    tcp {

       ### 定义多个上游服务器

       upstream  itmayeidu{

          ### 定义TCP模块上游服务器

          server 192.168.5.165:80001;

      server 192.168.5.165:80002;

       }

        server {

            listen       9999;

            server_name  192.168.212.137;

    ### 反向代理upstream

            proxy_pass itmayeidu;

        }

    }

     

    lvs 与Nginx 区别

    lvs工作在网络第四层,nginx工作在网络第七层;lvs比nginx抗负载能力强;lvs对网络依赖性强,nginx对网络依赖性弱;lvs几乎可以对所有应用做负载均衡,比如数据库。

     

    lvs与keepalived区别

    Lvs可以实现负载均衡,但是无法实现健康检查。Keepalived可以进行健康检查实现高可用。

     

    keepalived 作用

    keepalive 软件可以进行健康检查,而且能同时实现 LVS 的高可用性,解决 LVS 单点故障的问题

     

    如何实现双机主从热备

    Nginx+Tomcat:在upstream中配置多台服务器,从服务器后加backup

    Keepalived+Nginx:在多台nginx服务器上安装keepalived,将主服务器的state设置为MASTER,从服务器设置为BACKUP,主服务器的优先级要高于从服务器

     

    lvs+Keepalived+Nginx架构流程图

     

    项目发布如何不影响到正常用户访问,实现7*24小时访问

    可以两台机子互为热备,平时各自负责各自的服务。在做上线更新的时候,关闭一台服务器的tomcat后,nginx自动把流量切换到另外一台服务的后备机子上,从而实现无痛更新,保持服务的持续性,提高服务的可靠性,从而保证服务器7*24小时运行。

     

    项目如何发生故障宕机了,如何处理。

    使用lvs+keepalived+Nginx做主从热备,lvs管理nginx集群,nginx管理服务器集群,在服务器宕机的情况下keepalived启动健康检测,多次重启无果可以短信通知运维人员及时维护。

     

    动态网站与静态网站区别

    在浏览器中打开一个网站,点击鼠标右键查看源码,多次请求后如果源码不产生变化就是静态网站,变化就是动态网站。

     

    动态页面静态化的作用

    便于搜索引擎抓取和排名

     

    什么是动静分离架构模式

    静态页面与动态页面分开不同系统访问的架构设计方法,静态页面与动态页面以不同域名区分。

     

    如何搭建动静分离

    以nginx服务器作为静态资源服务器,静态资源和动态资源访问分开配置,静态资源在location中使用本地文件路径配置方式,动态资源使用proxy_pass配置到后台服务器。

    如:

        ###静态资源访问

        server {

          listen       80;

          server_name  static.itmayiedu.com;

          location /static/imgs {

               root F:/;

               index  index.html index.htm;

           }

        }

       ###动态资源访问

     server {

          listen       80;

          server_name  www.itmayiedu.com;

     

          location / {

             proxy_pass http://127.0.0.1:8080;

             index  index.html index.htm;

           }

        }

     

    动静分离与前后分离区别

    动静分离是将静态资源和动态资源存放在不同服务器中,前后分离是将前端和后台分离,前端通过api调用后台接口

     

    如何控制浏览器静态资源缓存

    静态资源存在缓存的原因是项目上线时,浏览器缓存中的静态资源导致与服务器将淘汰资源的代码发生冲突(或者是页面访问频繁访问同一资源,导致一些浏览器如IE(本人开发亲身经历过)返回默认的响应结果,与实际响应结果不符合),一般的服务器是强制F5进行刷新或者是清除缓存,最有效的解决方法就是在请求资源后面加上变量(如时间戳,随机数)

     

    Http状态码304的作用

    表示浏览器存在静态资源缓存就不从服务器获取静态资源

    展开全文
  • Java高并发面试题大全含答案

    千次阅读 2019-08-08 09:45:45
    好的并发设计就是充分利用现有资源,尽可能使处理器忙碌起来,当然程序设计始终不能脱离业务场景。 9 内存同步? 在synchronized和volatile提供的可见性保证中可能会用到内存屏障,内存屏障可以刷新缓存,使缓存...
    1. 1 Thread的start方法和run方法的区别? run方法就是普通的一个方法,代码运行在当前主线程,start会启动一个新的线程,并运行run方法。
      2 如何停止线程运行? 可以设置一个标志位,任务定期检查这个标记,如果标志设置为取消则任务停止执行,但已执行部分无法停止,标志变量最好设置为volatile。
      3 普通线程与守护线程? 本质都是线程没什么区别,守护线程在主线程结束时将被抛弃,自动退出。比如垃圾回收线程。
      4 数据库死锁? 在执行一个事务时可能要获取多个锁,一直持有锁到事务提交,如果A事务需要获取的锁在另一个事务B中,且B事务也在等待A事务所持有的锁,那么两个事务之间就会发生死锁。但数据库死锁比较少见,数据库会加以干涉死锁问题,牺牲一个事务使得其他事务正常执行。
      5 什么是锁顺序死锁? 两个线程试图以不同的顺序获得相同的锁,那么可能发发生死锁。比如转账问题,由from账户向to账户转账,假设每次我们先同步from对象,再同步to账户,然后执行转账操作,貌似没什么问题。如果这时候to账户同时向from账户转账,那么两个线程可能要永久等待了。
      6 死锁的避免与诊断? 如果一个线程最多只能获取一个锁,那么就不会发生锁顺序死锁了。如果确实需要获取多个锁,锁的顺序可以按照某种规约,比如两个资源的id值,程序按规约保证获取锁的顺序一致。或者可以使用显式的锁Lock,获取锁的时候设置超时时间,超时后可以重新发起,以避免发生死锁。
      7 线程饥饿与活锁? 当线程由于无法访问需要的资源而不能继续执行时,就是饥饿状态。活锁是线程虽然没有阻塞,但也不能继续执行,因为程序总是执行相同的操作,且结果都是失败。
      8 多线程性能问题? 使用多线程主要就是为了提高程序的运行性能,多线程可以更充分发挥系统可处理能力,从而提高系统资源利用率。但多线程自身同时带来了性能开销,线程的创建与销毁,线程间的协调(比如加锁、内存同步),线程调度,上下文的切换等。好的并发设计就是充分利用现有资源,尽可能使处理器忙碌起来,当然程序设计始终不能脱离业务场景。
      9 内存同步? 在synchronized和volatile提供的可见性保证中可能会用到内存屏障,内存屏障可以刷新缓存,使缓存无效。同时内存屏障会抑制一些编译器优化操作,大多数操作不能被重排序。
      10 jvm同步优化? jvm可以通过优化去掉一些不必要的锁,从而减少同步开销。比如一个对象只能被当前线程访问,其他线程不会与当前线程在这个锁上发生同步,jvm可以锁优化去掉同步操作。编译器也可以进行锁粒度粗化操作,将临近的多个同步代码用一个锁合并起来,不仅可以减少多个同步带来的不必要的开销,同时还能使优化器处理更大的代码块,带来进一步的优化。锁自旋,当线程发生阻塞时,可能会自旋等待(不断循环尝试去获取锁),或者通过操作系统挂起线程,当然这要看锁等待时间,来决定是否自旋。
      11 降低锁的竞争? 减少锁的持有时间,减少锁的请求频率,使用带有协调机制的独占锁。具体实现可以缩小锁的范围,快进快出。比如只锁同步操作代码块,不要把相关非同步业务逻辑也包含到同步代码块中。可以减小锁的粒度,能对目标对象进行上锁,就不要对操作目标对象的方法上锁,也可以使用一些锁分段技术的组件,比如ConcurrentHashMap。也可以使用一些非独占锁,比如ReadWriteLock。
      12 java中常见的同步机制? java主要同步机制是synchronized关键字, 还有显式的Lock,volatile,atomic,还有一些同步集合、阻塞队列等。
      13 共享变量在多线程下如何保证线程安全? 因为多线程是交替执行,每个线程操作共享变量时可能会导致数据不一致,要确保线程安全,需要在访问共享变量时添加同步机制。当然,如果这个变量本身是线程安全的,比如AtomicLong,那么多线程访问也是安全的。
      14 是否共享变量都使用类似AtomicLong原子安全类,多线程访问就是安全的? 这个不确定,因为无法保证多个变量同时操作,一个原子变量可以保证自己的安全性,但是同时操作多个有逻辑依赖原子的变量,仍可能带来线程安全问题。单个安全不代表组合也安全。
      15 synchronized锁的使用? synchronized同步代码块,修饰的方法是整个方法体,同步代码块的锁就是方法调用所在的对象实例;静态的synchronized方法以Class对象作为锁;还可以修饰具体对象,以具体对象为锁。
      16 可重入锁,synchronized是可重入的吗? 当一个线程拥有对象锁之后,如果再次访问此对象的同步代码块或对象时,不再需要获取锁,这就是可重入锁;synchronized是可重入的。
      18 volatile关键字的理解? volatile属于一个轻量的同步原语,被它修饰的变量,变量的更新操作可以通知到其他线程。简单来说,就是volatile修饰的变量不会被执行重排序,所以保证了变量的可见性。但volatile无法保证变量的原子性操作,仍然是不安全的,通常可以用作标志位,如退出线程的循环变量。
      19 final修饰的不可变对象? 由关键字final修饰的对象是不可变的,不能被重新赋值,但是final仍可以修饰可变对象的引用,例如集合:final修饰的集合本身引用地址不能改变,但是集合内的数据还是可以修改的。不可变对象会减少加锁或保护性副本的需求,可以带来一些性能上的优势。
      20 常见的并发容器? ConcurrentHashMap:使用了分段锁,锁的粒度变得更小,多线程访问时,可能都不存在锁的竞争,所以大大提高了吞吐量。简单对比来看,就好比数据库上用行锁来取代表锁,行锁无疑带来更大的并发。
      CopyOnWriteArrayList:写入时复制,多线程访问时,彼此不会互相干扰或被修改的线程所干扰,当然copy时有开销的,尤其时列表元素庞大,且写入操作频繁时,所以仅当迭代操作远远大于修改操作时,才应该考虑使用。
      BlockingQueue:阻塞队列提供了可阻塞的put和take方法,当队列已经满了,那么put操作将阻塞到队列可用,当队列为空时,take操作会阻塞到队列里有数据。有界的队列是一种强大的资源管理器,可以在程序负荷过载时保护应用,可作为一种服务降级的策略。阻塞队列还提供offer操作,当数据无法加入队列时,返回失败状态,给应用主动处理负荷过载带来更多灵活性。
      21 常见的同步工具类? CountDownLatch:递减计数器闭锁,直到达到某个条件时才放行,多线程可以调用await方法一直阻塞,直到计数器递减为零。比如我们连接zookeeper,由于连接操作是异步的,所以可以使用countDownLatch创建一个计数器为1的锁,连接挂起,当异步连接成功时,调用countDown通知挂起线程;再比如5V5游戏竞技,只有房间人满了才可以开始游戏。
      FutureTask:带有计算结果的任务,在计算完成时才能获取结果,如果计算尚未完成,则阻塞 get
      方法。FutureTask将计算结果从执行线程传递到获取这个结果的线程。
      Semaphore:信号量,用来控制同时访问某个特定资源的数量,只有获取到许可acquire,才能够正常执行,并在完成后释放许可,acquire会一致阻塞到有许可或中断超时。使用信号量可以轻松实现一个阻塞队列。
      CyclicBarrier:类似于闭锁,它可以阻塞一组线程,只有所有线程全部到达以后,才能够继续执行,so线程必须相互等待。这在并行计算中是很有用的,将一个问题拆分为多个独立的子问题,当线程到达栅栏时,调用await等待,一直阻塞到所有参与线程全部到达,再执行下一步任务。
      22 线程’饥饿’死锁 在线程池中,如果一个任务依赖其他任务,那么可能会导致线程死锁。比如在单线程的Executor中,一个任务将另一个任务提交到Executor中,等待这个任务的处理结果。第一个任务在等待第二个任务,而第二个任务在队列里等待第一个任务执行完成。
      23 ThreadPoolexecutor参数配置? corePoolSize- 池中所保存的线程数,包括空闲线程。 maximumPoolSize - 池中允许的最大线程数。 keepAliveTime-
      当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 unit - keepAliveTime 参数的时间单位。
      workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
      threadFactory- 执行程序创建新线程时使用的工厂。 handler -
      由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
      如果池中运行的线程小于coreSize,那么新的请求将创建新的线程,如果coreSize线程全部忙碌,新请求将被添加到队列,如果队列已满,那么将继续创建新线程,但线程总数<=maximumPoolSize,多余coreSize的线程会在超过keepAliveTime终止。
      24 线程池任务饱和时处理策略? 当线程池线程数已经达到最大值,注意这时队列一定已经满了,线程池已经处于饱和状态,那么新来的请求将会按照相对应的策略被处理。
      AbortPolicy:拒绝任务,抛出RejectedExecutionException CallerRunsPolicy:在
      execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。 DiscardPolicy:直接丢弃。
      25 什么是Executor? Executor执行已提交的Runnable任务,把任务和执行机制分离开来,可以看作是一个任务执行框架,任务与执行的解耦,可以更灵活的指定执行方式。所以应该使用Executor代替new
      Thread(Runnable).start()。
      26 什么是线程池,如何创建线程池? 线程池就是由一组活跃的线程集合,由于线程的创建与销毁开销都比较大,所以利用线程池减少线程的性能开销,提高响应性。适当的调节线程池大小,可以有效利用处理器,同时防止过多线程相互竞争资源耗费内存。使用Executors可以很方便的创建线程池。
      27 Executors可以创建哪些类型的线程池? newFixedThreadPool:创建一个固定大小的线程池,每当提交一个任务就创建一个线程,直到达到最大数量,达到最大线程数后线程规模不再变化。
      newCachedThreadPool:创建一个可以根据需要创建新线程的线程池,当线程规模大于处理请求时,将回收空闲线程,当需求增加时,可以添加新的线程。
      newSingleThreadExecutor:创建一个单线程Executor。
      newScheduledThreadPool:创建一个固定长度,以延迟或定时的执行方式执行任务。
      28 Executor的生命周期? ExcutorService扩展了Executor,有三种状态:运行,关闭,已终止。可以调用shutdown方法,优雅关闭任务,这时将不再接受新的任务,同时等待已提交的任务执行完成。
    展开全文
  • 在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
  • java 高并发面试题

    千次阅读 2018-11-02 16:09:31
    整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。 cas  Java中Unsafe类详解 12、线程池 线程池的作用:  在程序启动的时候...
  • 多线程和高并发的常见面试题整理

    千次阅读 2020-07-12 11:46:03
    消息队列的可靠性怎么保证 了解高并发吗,多线程里怎么保证线程安全 Fork/Join Fork/Join就是利用了分治的思想组建的框架,平日里很多场景都能利用到分治思想。框架的核心ForkJoinPool,因为含有任务队列和窃取的...
  • 多线程面试题(值得收藏)

    万次阅读 多人点赞 2019-08-16 09:41:18
    金九银十快到了,即将进入找工作的高峰期,最新整理的最全多线程并发面试47和答案总结,希望对想进BAT的同学有帮助,由于篇幅较长,建议收藏后细看~ 1、并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,...
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 本套Java面试题大全,全的不能再全,哈哈~ 一、Java 基础 1. JDK 和 JRE 有什么区别? JDK:Java ...
  • 面试经典情景高并发解决方案

    千次阅读 多人点赞 2019-09-21 11:54:19
    面试经典情景高并发解决方案 情景模拟:在很多个用户同时访问网站的时候,例如:抢购或者双十一的时候。如何避免服务器宕机或者数据库挂掉的问题,请你提供几种解决方案。 1、静态资源与后台服务进行分离 ...
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    Java面试总结(2021优化版)已...https://thinkwon.blog.csdn.net/article/details/104390689 4 并发编程面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992 5 JVM面试题(2020最新版) ...
  • 消息中间件MQ与RabbitMQ面试题(2020最新版)

    万次阅读 多人点赞 2020-03-01 11:11:21
    Java面试总结(2021优化版)已发布在个人微信公众号【技术人成长之路】,优化版首先修正了读者反馈的部分答案存在的错误,同时根据最新面试总结,删除了低频问题,添加了一些常见面试题,对文章进行了精简优化,欢迎...
  • java面试题_高并发、高可用、分布式(9题)
  • 15个PHP关于高并发面试题(总结)

    千次阅读 2020-10-22 09:51:20
    774 一 、PHP基础部分 1、PHP语言的一大优势是跨平台,什么是跨平台? PHP的运行环境最优搭配为Apache+MySQL...2、WEB开发中数据提交方式有几种?...(2)可缓存性:get 方式是可以缓存的,post 方式不可以缓存。...(3.
  • 互联网高并发解决方案相关面试题

    千次阅读 2019-09-07 21:33:36
    高并发服务限流特技有哪些算法? 传统计算器算法,滑动窗口计数器算法,令牌桶算法和漏桶算法。 传统计数器限流算法有什么弊端? 传统计数器限流方式不支持高并发,存在线程安全问题.若大量访问请求集中在计数器最后...
  • 高并发是从业务角度去描述系统的能力,实现高并发的手段可以采用分布式,也可以采用诸如缓存、CDN等,当然也包括多线程; ● 多线程则聚焦于如何使用编程语言将CPU调度能力最大化。 转载于:...
  • 并发原则 ⽆状态 ⽆状态应⽤,便于⽔平扩展 有状态配置可通过配置中⼼实现⽆状态 实践: Disconf、Yaconf、Zookpeer、Consul、Confd、Diamond、 Xdiamond等 拆分 系统维度:按照系统功能、业务拆分,如购物⻋,结算...
  • SpringCloud面试题(一)

    万次阅读 多人点赞 2019-04-24 22:16:30
    SpringCloud面试题(一) 大家好,我是酷酷的韩~下面提供一些整理的springcloud面试题 一.微服务的优点缺点?说下开发项目中遇到的坑? 优点: 1.每个服务直接足够内聚,代码容易理解 2.开发效率,一个服务只做一件事...
  • MyBatis面试题(2020最新版)

    万次阅读 多人点赞 2019-09-24 16:40:33
    整理好的MyBatis面试题库,史上最全的MyBatis面试题,MyBatis面试宝典,特此分享给大家 MyBatis 介绍 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC ...
  • 1. ⾼并发原则 ⽆状态: ⽆状态应⽤,便于⽔平扩展 有状态配置可通过配置中⼼实现⽆状态 实践: Disconf、Yaconf、Zookpeer、Consul、Confd、Diamond、Xdiamond等 拆分: 系统维度:按照系统功能、业务拆分,如购物...
  • 并发编程面试题(2020最新版)

    万次阅读 多人点赞 2020-03-14 17:28:01
    文章目录基础知识并发编程的优缺点为什么要使用并发编程(并发编程的优点)并发编程有什么缺点并发编程三要素是什么?在 Java 程序中怎么保证多线程的运行安全?并行和并发有什么区别?什么是多线程,多线程的优劣?...
  • 一份经典多线程并发面试题

    千次阅读 2019-05-07 19:49:28
    面试中关于 synchronized 关键字的 5 连击 1.1 说一说自己对于 synchronized 关键字的了解 synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在...
  • 面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不少人都会分布式、高并发、多线程将三者混淆,误以为所谓的分布式高并发...
  • 文章目录1、高并发架构相关概念1)并发2)我们说的高并发是什么?3)高并发的问题,我们具体该关心什么?4)常用性能测试工具① ab② wrk③ http_load④ Web Bench⑤ Siege⑥ Apache JMeter5)QPS达到极限,该怎么办...
  • 花费数天时间整理的常见大厂问的高并发常见面试题,里面有代码(深入底层+代码解读)
  • 高并发面试总结

    万次阅读 多人点赞 2018-07-07 18:28:58
    好处是在保证合理的同步前提下,效率很。坏处是:严格来说,读取操作不能保证反映最近的更新。例如线程A调用 putAll() 写入大量数据,期间线程B调用 get() ,则只能 get() 到目前为止已经顺利插入的部分数据。 ...
  • Redis与系统高并发之间的关系二. Redis单机的瓶颈三. Redis如何支撑超过10万QPS的并发量四. Redis replication核心机制 一. Redis与系统高并发之间的关系 想要设计一套能承载数十万甚至上百万QPS的系统,光凭Redis...
  • c++并发面试题

    千次阅读 2019-05-13 18:22:13
    秒杀多线程第一篇 多线程笔试面试题汇总: https://blog.csdn.net/morewindows/article/details/7392749 50个多线程面试题,你会多少? https://blog.csdn.net/cmyperson/article/details/79610870 多线程的...
  • MySQL 面试题

    万次阅读 多人点赞 2019-09-02 16:03:33
    MySQL 面试题 MySQL 涉及的内容非常非常非常多,所以面试题也容易写的杂乱。当年,我们记着几个一定要掌握的重心: 重点的题目添加了【重点】前缀。 索引。 锁。 事务和隔离级别。 因为 MySQL 还会有部分内容和...
  • 总结java高级面试题

    万次阅读 多人点赞 2019-05-10 16:25:39
    jvm结构原理,GC工作原理 Jvm结构: Jvm主要包括四个部分: 1、类加载器(ClassLoad) 在JVM启动时或者在类运行时将需要的class加载到JVM中。...类从被加载到虚拟机内存开始,在到卸载出内存为止,正式生命周期...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,829
精华内容 37,931
关键字:

高并发面试题