多线程问题面试归纳

1.用Runnable还是Thread?

因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,继承是很昂贵的资源,面向接口编程也是设计模式6大原则的核心。

2.start()方法和run()方法的区别

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

3.Runnable接口和Callable接口的区别

Runnable从JDK1.0开始就有了,Callable是在 JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

4.什么是线程安全?Vector是一个线程安全类吗?

果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

5.java中如何停止一个线程?

Thread类提供的stop方法太过暴力,会导致数据不一致问题,但是我们可以自行决定何时退出。我们可以在线程中设置一个标记变量,在while(true)循环中,需要退出时就将该变量修改为true,并在if判断中break退出循环即可。

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

通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。

7.sleep方法和wait方法有什么区别

这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器。

8.Java中notify 和 notifyAll有什么区别?

二者基本功能一直,但是notify会随机选一个线程唤醒而notifyall会唤醒等待队列中所有的等待线程,而不是随机一个。

9.生产者消费者模型的作用是什么?

1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用

2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约
10.为什么要使用线程池?
免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目

11.Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来 检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛 出InterruptedException异常的方法都会将中断状态清零。

12.为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用?

这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁。

13.ConcurrentHashMap的并发度是什么?

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

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

它会使当前线程让出cpu,但是并不表示当前线程不执行了。他还是会进行cpu争夺,但是否能被再次分配就不一定了。

15.怎么唤醒一个阻塞的线程?

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它

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

这里区分一下:

1)如果使用的是无界队列LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务

2)如果使用的是有界队列比如ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,会根据maximumPoolSize的值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockingQueue继续满,那么则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy

17.什么是Java内存模型?

Java内存模型定义了一种多线程访问Java内存的规范,指定了java虚拟机如何与操作系统内存进行。Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几部分内容:

1)Java内存模型将内存分为了主内存和工作内存。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,会将最新的值更新到主内存中去

2)定义了几个原子操作,用于操作主内存和工作内存中的变量

3)定义了volatile变量的使用规则

4)happens-before,即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、解锁必然发生在随后的加锁前,线程的start方法先于他的每一个动作等,只要符合这些规则,则不需要额外做同步措施,这些原则是为了保证指令重排后不会破坏原有的语义结构。

18.什么是volatile?

volatile关键字的作用主要有两个:

1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据。

2)现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile直接禁止JVM和处理器对该关键字修饰的指令重排序,当然这也一定程度上降低了代码执行效率。

19.什么是乐观锁悲观锁?

乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不加锁,一旦发生冲突就重试,直到成功为止。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

2)悲观锁:其实可以吧synchronized当做一种悲观锁,还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了

20.创建线程池的关键参数有哪些?

1.corePoolSize:线程池中的常驻核心线程数

在创建了线程池后,当有请求任务进来之后,就会安排池中的线程去执行请求任务,近似理解为今日当值线程

当线程池中的线程数目达到corePoolSize后,就会把到达的任务放入缓存队列中。

2.maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1

3.keepAliveTime:多余的空闲线程的存活时间。

当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,

多余空闲线程会被销毁直到只剩下corePoolSize个线程为止

默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池找那个的线程数不大于corePoolSize。

4.unit:keepAliveTime的单位

5.workQueue:任务队列,被提交但尚未被执行的任务

6.threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般默认即可

7.handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池最大线程数(maximumPoolSize)时如何处理

21.ConcurrentHashMap原理:

1.7版本:
并发安全,引入了分段锁机制,底层分了16段,理论上并发性能比hash高16倍,每个segment可以看做一个hashtable,某个线程在操作k,v时,会锁住此kv存在的segment,而不锁住其他的segment。此外,其存取方法和hashmap一致,负载因子的概念也一致,底层的链表结构也和hashmap一致。
1.8版本:CAS无锁算法:实际上是一组cpu指令,同步代码块其实是一种独占方式,线程的挂起和恢复都需要很大的开销,他其实是一种乐观锁,当多个线程使用cas操作一个变量时,只有一个线程能跟新变量的值,其他线程全部失败,但是失败线程不会被挂起,而是再次尝试修改。CAS有三个操作数,内存值 V,旧的预期值A,要修改的新值B,当且仅当V=A时,才会将V修改为B,否则什么都不做。她有一个语义:我认为V的值应该是A,如果是,就将V的值更新为B,否则,不修改并告诉V的值实际为多少。优点在于没有引入锁机制,避免上锁释放锁的开销,以及上下文切换的开销。可能存在问题:可能导致冲突,所以失败重试次数很多。
在这里插入图片描述

22.HashTable:

hashtable底层所有的方法都上锁,

23.线程池有哪几种任务拒绝策略?

1.AbrotPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
2.CallerRunsPolicy策略:如果线程池未关闭,直接调用当前被抛弃的任务。
3.DiscardOledestPolicy:丢弃最老的请求,并尝试再次提交当前任务。
4.DiscardPolicy:直接丢弃,不做任何处理。

24.java内存模型的八种基本操作?

(1)lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
(2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
(3)read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
(4)load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
(5)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
(6)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
(7)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
(8)write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中

25.什么是双亲委托?

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

26.有几种类加载器?

根加载器:由C++编写,用来加载JVM核心类库
拓展类加载器:用来加载JAVA_HOME路径下jre/lib/ext目录中的类库。
系统类加载器:是一种常见的类加载器,加载classpath下的类库

27.双亲委托机制解决的什么问题?

防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。

28.了解过RPC吗?

远程过程调用,是分布式系统常见的一种通信方法,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果,客户端发起请求,服务器返回响应(类似于Http的工作方式)RPC在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

29.Lock与Synchronized的区别

Lock与synchronized有以下区别:
synchronized底层使用指令码方式来控制锁的,Lock:底层是CAS乐观锁。

Lock是一个接口,而synchronized是关键字。
synchronized会自动释放锁,而Lock必须手动释放锁。
Lock可以让等待锁的线程响应中断,使用lockInterruptibly(),而synchronized不会,线程会一直等待下去。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
lock可以使用tryLock方法等待一段时间,如果在规定时间内没有获取锁,那就可以不用等待。
synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);
Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

30.Synchronized做了哪些优化?

1.适应自旋锁

自旋锁:为了减少线程状态改变带来的消耗 不停地执行当前线程

2.锁消除:

不可能存在共享数据竞争的锁进行消除

3.锁粗化:

将连续的加锁 精简到只加一次锁

4.轻量级锁:

无竞争条件下 通过CAS消除同步互斥

5.偏向锁:

无竞争条件下 消除整个同步互斥,连CAS都不操作。

31.进程与线程的区别

  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;

  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信

号等),某进程内的线程在其他进程不可见;

  1. 调度和切换:线程上下文切换比进程上下文切换要快得多
  2. 一个标准的线程由线程ID,当前指令指针PC,寄存器和堆栈组
    成。而进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值