高并发面试总结

面试专题 · 2019-05-29 · 205 人浏览

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. Vector、SimpleDateFormat是线程安全类吗

Vector类的单个方法是原子性的,符合操作不是原子性的 
SimpleDateFormat类不是线程安全的

5. 哪些集合类是线程安全的

  • 1.Vector

  • 2.Stack

  • 3.hashtable

  • 4.enumeration

  • 5.StringBuffer

6. 多线程中忙循环是什么

忙循环就是程序员用循环让一个线程等待,不像传统方法

wait()
sleep()
或者
yied()
它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。

7. 什么是线程局部变量

ThreadLocal并非是一个线程本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更合适。线程局部变量(ThreadLocal)功能非常简单,就是为每一个使用该变量的线程都提供了一个变量值副本,是java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。

8. 进程间如何通讯

  • 管道(pipe)

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

  • 有名管道(namedpipe)

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

  • 信号量(semaphore)

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

  • 消息队列(messagequeue)

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

  • 信号(signal)

    • 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生

  • 共享内存(shared memory)

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

  • 套接字(socket)

    • 套接口也是一种进程间通信机制,以其他通信机制不同的是,它可用于不同进程间的通信

9. 线程间如何通讯

  • 锁机制:包括互斥锁、条件变量、读写锁

    • 互斥锁提供了以排他方式防止数据结构被并发修改的方法

    • 读写锁允许多个线程同时读共享数据,而对写操作是互斥的

    • 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

  • 信号量机制:包括无名线程信号量和命名线程信号量

  • 信号机制:类似进程间的信号处理

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

10. 什么是多线程环境下的伪共存(false sharing)

缓存系统中是以缓存行为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存航,就会无意中影响彼此的性能,这就是伪共存

11. 同步和异步有何不同,在什么情况下分别使用它们?举例说明

  • 如果数据将在线程间共享。例如:正在写的数据以后可能会被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取

  • 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效。

同步交互:指发送一个请求,需要等待返回,然后才能发送下一个请求,有个等待的过程 
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。 
区别:一个需要等待,一个不需要等待

12. 
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算法实现无锁化的修改至操作,他可以大大降低锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。

13. 
Hashtable
Hashmap
的区别

  • HashTable
    Hashmap
    都实现了Map接口,但是
    Hashtable
    的实现是基于
    Dictionary
    抽象类。

  • HashMap
    中,null可以作为键,这样的键只能有一个;可以有一个或者多个键所对应的值为null;当
    get()
    方法返回null时,既可以表示
    Hashmap
    中没有该键,也可以表示该键锁对应的值为null。因此,在
    Hashmap
    中不能由
    get()
    方法来判断
    HashMap
    中是否存在某个键,而应该使用
    containsKey()
    方法来判断。

  • HashTable
    中,无论键key还是值value都不能为null

  • 这两个 类最大的不同之处在于: 

    • HashTable
      是线程安全的,它的方法是同步的,可以直接用于多线程环境中

    • HashMap
      是线程不安全的,在多线程环境中,需要手动实现同步机制

14. 
ArrayBlockingQueue
的用法

一个线程向一个固定大小的队列里面不停地存放数据,另一个线程不停地向这个队列里面取数据,当队列满了,还继续存放数据,此时出现阻塞,直到队列有空闲的位置;反之,当队列为空,还继续取数据,则也出现阻塞,直到队列中有数据为止

15. 
CountDownLatch
的用法

  • CountDownLatch
    可以控制线程的执行,它可以让所有持有它的多个线程同时执行,也可以控制单个线程执行。

  • 它在初始化的时候会传入一个int类型的参数

    i
    ,调用一次
    countDown
    方法后
    i
    的值回减1。

  • 在一个线程中如果调用了

    await()
    方法,这个线程就会进入等待的状态,当参数i为为0的时候这个线程才继续执行

  • 例子: 比如一个跑步比赛,有5个选手参加,有两点需要注意,第一我们必须确保这5个选手都准备就绪了,才能宣布比赛开始,第二只有当5个选手都完成比赛了才能宣布比赛结束。 所以假设这5个选手都有一个独立的线程进行跑步,那么每个线程都必须持有一个相同的

    CountDownLatch
    ,并且调用
    await()
    进入等待状态,然后才能确保他们同时开始比赛。 同时可以主线程中也要持有一个
    CountDownLatch
    ,当选手开始比赛后要进入等待状态,等待所有的选手都完成比赛才能宣布比赛结束。

16. 
CountDownLatch
CyclicBarrier
有什么不同?各自内部原理和用法是什么

  • CountDownLatch
    可以理解为倒计时锁,这个类能够使一个线程等待其他县城完成各自的工作后再执行,应用程序的主线程希望在负责启动框架服务的线程已经启动所有框架服务后再执行。
    CountDownLatch
    不可以重新使用

  • CyclicBarrier
    可以看成是障碍,所有线程必须到齐后才能一起通过这个障碍。场景:公司组织户外拓展活动,帮助团建,其中最重要的一个项目就是全体员工在完成其他项目时,到达一个高达4米的高墙,没有任何抓点,要求所有人,一个不能少的越过高墙,才能继续进行其他项目。
    CyclicBarrier
    可以重新使用

17. 线程和进程有什么区别

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

18. 用
Runnable
还是用
Thread

大家知道我们可以通过继承

Thread
类或者调用
Runnable
接口来实现线程,问题是,哪个方法更好呢?什么情况下使用哪种呢?如果你要继承其他的类,就实现
Runnable
接口

19. 
Thread
类中的
strat()
run()
方法有什么区别?

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

20. java中
Runnable
Callable
的区别

  • Runnable和Callable都代表那些要在不同的线程中执行的任务。

    Runnable
    从JDK1.0开始就有了,
    Callable
    是在JDK1.5增加的。

  • 他们的主要区别是Callable的

    call()
    方法可以返回值和抛出异常,而Runnable的
    run()
    方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

21. 什么是java内存模型

java内存模型定义了java虚拟机在计算机内存中的工作方式。JMM决定了一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每一个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

22. 什么是线程安全?

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

23. java中什么是竞态条件

当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。换句话说,就是正确的结果要取决于运气。最常见的竞态条件类型就是“先检查后执行”操作,即通过一个可能失败的观测结果来决定下一步的动作。

24. java中如何停止一个线程

java提供了很丰富的API但没有为停止线程提供API。JDK1.0本来有一些像

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

25. 一个线程运行时发生异常会怎样?

  • 如果异常没有被捕获该线程将会停止执行。

  • Thread.UncaughtExceptionHandler
    是用于处理未捕获异常造成线程突然中断情况的一个内嵌借口。当一个未捕获异常将造成线程中断的时候JVM会使用
    Thread.getUncaughtExceptionHandler
    来查询线程的
    UncaughtExceptionHandler
    并将线程和异常作为参数传递给handler的
    uncaughtException()
    方法进行处理

26. 如何在两个线程间共享数据?

可以通过共享对象来实现这个目的,或者是使用阻塞队列,或者使用

wait()
notify()
方法

27. 锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象的锁,而其它的线程想要调用这个对象的某个

    Synchronized
    方法(或者
    Synchronized
    代码块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这个线程就进入了该对象的锁池中

  • 等待池:假设一个线程A调用了某个对象的

    wait()
    方法,线程A就会释放该对象的锁后,进入到该对象的等待池中。

28. java中的
notify
notifyAll
有什么区别?

  • 如果线程调用了对象的

    wait()
    方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁

  • 当有线程调用了对象的

    notifyAll()
    方法(唤醒所有wait线程)或者
    notify()
    方法(只随机唤醒一个wait线程),被唤醒的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了
    notify
    方法后只要一个线程会由等待池进入锁池,而
    notifyAll
    方法会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用

    wait()
    方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

  • notify可能会产生死锁

29. 为什么wait、notify和notifyAll这些方法不再thread类里面

这是个设计相关的问题,它考察的是面试者对现有系统和一些普遍存在但看起来不合理的事务的看法。回答这类问题的时候,你要说明为什么把这些方法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是java提供的锁时对象级的而不是线程级的,每个对象都有所,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在thread类中,线程正在等待的是哪个锁就不明显了。

30. 什么是
FutureTask
?

在java并发程序中

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

31. java中
interrupted
isInterrupted
方法的区别

  • interrupted()
    isInterrupted()
    的主要区别是前者会将当前线程的中断状态清除而后者不会。

  • java多线程的中断机制是用内部标志来实现的,调用

    Thread.interrupt()
    来中断一个线程就会设置中断标志为true。当中断线程调用静态方法
    Thread.interrupt()
    来检查中断中断时,中断状态会被清零。而非静态方法
    isInterrupted()
    用来查询其他线程的中断状态且不会改变中断状态标识。

32. 为什么wait和notify方法要在同步快中调用?

  • 主要是因为java API强制要求这样做,如果你不这样做,你的代码会抛出

    IllegalMonitorStateException
    异常。

  • 为了避免wait和notify之间产生竞态条件

33. 为什么你应该在循环中检查等待条件

处于等待状态的线程可能会受到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下推出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因。

34. java中堆和栈有什么不同?

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

35. 什么是线程池?为什么要使用它?

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

36. 如何避免死锁?

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

  • 互斥条件:一个资源每次只能被一个进程使用

  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

避免死锁最简单的方法就是阻塞循环等待条件,将系统中所有资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或者降序)做操作来避免死锁

37. java中活锁和死锁有什么区别?

活锁和死锁类似,不同之处在于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子就是:两个人在狭小的走廊碰到,两个人都试着避让对方好让彼此通过,但是因为避让的方向都一直导致最后谁都不能通过走廊。简单的说就是,活锁和死锁的主要区别是前者进程的状态可以改变但是却不能继续执行。

38. 怎么检测一个线程是否拥有锁?

java.lang.Thread
中有一个方法叫
holdsLock()
方法,当且晋档当前线程拥有某个具体对象的锁时它返回true。

39. 如果在java中获取线程堆栈?

对于不同的操作系统,有很多方法来获得java进程的线程堆栈。当你获取线程堆栈时,JVM会把所有线程的状态存到日志文件或者输出到控制台。在Windows下可以使用Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令

也可以使用jstack这个工具来获取,它对线程id进行操作,可以用jps工具找到id

40. java中synchronized和ReentrantLock有什么不同?

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

41. 有三个线程T1、T2和T3,怎么确保它们按照顺序执行?

在多线程中有多重方法让线程按特定的顺序执行,你可以用线程类的

join()
方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成

42. Thread类中的yield方法有什么作用?

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

43. java中
ConcurrentHashMap
的并发度是什么?

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

44. java中的Semaphore是什么?

java中的Semaphore是一种新的同步类,它是一个记数信号。从概念上将,信号量维护了一个许可集合。如果必要,在许可可用前会阻塞每一个

acquire()
,然后再获取该许可。每个
release()
添加一个许可,从而可能释放一个正在阻塞的获得者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行技术,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。

45. 如果你提交任务时,线程池队列已满时会发生什么?

如果一个任务不能被调度执行那么

ThreadPoolExecutor
submit()
方法将会抛出一个
RejectedExecutionException
异常

46. java线程池中
submit()
execute()
方法有什么区别?

两个方法都可以向线程池提交任务,

execute()
方法的返回类型时void,它定义在Executor接口中,而
submit()
方法可以返回持有计算结果的Future对象,它定义在
ExecutorService
接口中,它扩展了
Executor
接口。

47. 什么是阻塞式方式?

阻塞式方式是指程序会一直等待该方法完成期间不做其他事情,

ServiceSocket
accept()
方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到等到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

48. java中的
ReadWriteLock
是什么?

一般而言,读写锁是用来提升并发程序性能的锁分离技术的成果。java中的ReadWriteLock是java5中新增的一个接口,一个

ReadWriteLock
维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程持有。写锁是独占的,你可以使用JDK中的
ReentrantReadWriteLock
来实现这个规则

49. 
volatile
变量和
atomic
变量由什么不同?

首先

volatile
变量和
atomic
变量看起来很像,但是功能却不一样。

  • volatile
    变量可以确保线性关系,即写操作会发生在后续的读操作之前,但它不能保证原子性。例如用volatile修饰count变量,那么count++操作就不是原子性的。

  • AtomicInteger类提供的atomic方法可以让这种操作具有原子性,如:

    getAndIncrement()
    方法会原子性的进行增量操作把当前值加1.,其他数据类型和引用变量也可以进行相似操作。

50. 如果同步块内的线程抛出异常会发生什么?

无论同步块是正常是还异常退出,里面的线程都会释放锁。

51. 单例模式的双检锁是什么?

52. 写出3条你遵循的多线程最佳实践

  • 给你的线程起个有意义的名字

    • 这样可以方便找bug或者追踪。给线程起一个和它要完成的任务相关的名字,所有的主要框架甚至JDK都遵循这个最佳实践

  • 避免锁定和缩小同步的范围

    • 锁花费的代价高昂且上下文切换更耗费实践空间,试试最低限度的使用同步和锁,缩小临界区。

  • 多用同步类少用wait和notify

    • 首先,CountDownLatch、Semaphore、CyclicBarrier和Exchanger这些同步类简化了编码操作,而用wait()和notify()很难实现对复杂控制流的控制。其次,这些类是由最好的企业编写和维护在后续的JDK中它们还会不断优化和完善,使用这些更高等级的同步工具你的程序可以不费吹灰之力获得优化。

  • 多用并发集合少用同步集合

    • 并发集合比同步集合的可扩展性更好,所以在并发编程时使用并发集合效果更好。如果下次你需要用map,你应该首先想到用ConcurrentHashMap

53. 如何强制启动一个线程?

这个问题就像是如何强制进行Java垃圾回事,目前还没有方法,虽然你可以使用

System.gc()
来进行垃圾回收,但是不保证能成功。在java里面没有办法强制启动一个线程,它是被线程调度器控制着且java没有公布相关的API

54. java中fork join框架是什么?

fork join框架是JDK 7 中出现的一款高效的工具,java开发人员可以通过它充分利用现代服务器上的多处理器。它是专门为了那些可以地柜划分成许多子模块设计的,目的是将所有可用的处理能力用来提升程序的心梗。fork join框架一个巨大的优势是它使用了工作窃取算法,可以完成更多任务的工作线程可以从其他线程中窃取任务来执行

55. java多线程中调用wait()和sleep方法有什么不同?

java程序中wait和sleep都会造成某种形式的暂停,它们可以满足不同的需要。

wait()
方法用于线程间通信,如果等待条件为真切其他线程被唤醒时它会释放锁,而
sleep()
方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。

56. 如何强制启动一个线程?

在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,只有该线程拥有CPU的执行权,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

57. 什么是线程组,为什么在Java中不推荐使用

虽然县城组看上去很有用处,实际上现在的程序开发中已经不推荐使用它了,主要有两个原因:

  • 线程组TheadGroup对象中比较有用的方法是

    stop()
    resume()
    suspend()
    等方法,由于这几个方法会导致线程的安全问题(主要是死锁的问题),已经被官方废弃了,所以线程组本身的应用价值就大打折扣

  • 线程组ThreadGroup不是线程安全的,这在使用过程中获取的信息并不全是及时有效的,这就降低了它的统计使用价值

虽然线程组现在已经不被推荐使用了,但是它在线程的异常处理方面还是做出了一定的贡献。当线程运行过程中出现异常情况时,在某些情况下JVM会把线程的控制权交到线程关联的线程组对象上来进行处理。

58. 有哪些不同的线程生命周期

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

  • 新建状态:

参考并感谢

[1] https://www.cnblogs.com/xiaomaozi168/p/6972174.html 
[2] https://blog.csdn.net/u013136708/article/details/49444459 
[3] https://www.cnblogs.com/Jansens520/p/8624708.html 
[4] https://blog.csdn.net/JKL852qaz/article/details/79915159


高并发