毕业季:Java多线程面试题

Java多线程模块

1.什么是线程安全?

a) 代码在多线程下执行和在单线程下执行永远都能获得一样的结果,代码就是线程安全的

2.线程安全类

a) 早期的线程安全类

​ i. vector:比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用

​ ii. statck:堆栈类,先进后出

​ iii. hashtable:就比hashmap多了个线程安全

​ iv. enumeration:枚举,相当于迭代器

b) Collections包装方法:Collections针对每种集合都声明了一个线程安全的包装方法,在原集合的基础上添加了锁对象,集合中的每个方法都通过这个锁对象实现同步

c) java.util.concurrent包中的集合

​ v. ConcurrentHashMap

​ vi. CopyOnWriteArrayList和CopyOnWriteArraySet:都是加了写锁的ArrayList和ArraySet,锁住的是整个对象,但读操作可以并发执行

​ vii. ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等

3.ConcurrentHashMap和HashTable的区别

a) 都是线程安全的集合

b) 不同主要是加锁粒度上的不同;HashTable的加锁方法是给每个方法加上synchronized关键字,这样锁住的是整个Table对象。而ConcurrentHashMap是更细粒度的加锁,直接在table元素上加锁,实现对每一行进行加锁,进一步减小了并发冲突的概率

4.为什么没有ConcurrentArrayList线程安全类

a) 无法设计一个通用的而且可以规避ArrayList的并发瓶颈的线程安全的集合类,只能锁住整个list,这用Collections里的包装类就能办到

b) ArrayList的并发瓶颈无法确定:问题在于,像“Array List”这样的数据结构,你不知道如何去规避并发的瓶颈。拿contains() 这样一个操作来说,当你进行搜索的时候如何避免锁住整个list

5.线程的生命周期(五个阶段:新建状态、就绪状态、运行状态、阻塞状态、销毁状态)

a) 新建状态:刚new出来的线程

b) 就绪状态:调用start()方法后,等待获取CPU资源

c) 运行状态:处于就绪状态的线程获取到CPU资源后调用run()方法

d) 阻塞状态:运行状态的线程遇到某些原因进入阻塞状态,例如sleep()/wait()/IO操作等等。当阻塞的线程被唤醒,线程将进入就绪状态,重新等待CPU分配资源

e) 销毁状态:线程正常执行完毕、遇到异常或强制退出时线程处于销毁状态

记得周期图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9fMMAfY-1575013023222)(file:///C:/TEMP/msohtmlclip1/01/clip_image002.png)]

6.sleep()和yield()的区别

a) sleep()使线程进入阻塞状态,yield()使线程进入就绪状态

b) sleep()使得其他无论什么优先级的线程都可能运行,yield()只有比当前线程优先级高的线程才有可能运行,包括自己

c) sleep()需要捕获异常,yield()不需要

7.join()方法的作用

a) 等待线程一定时间或者等待线程执行完毕后再执行join后面的代码

8.synchronized关键字

a) synchronized修饰方法或代码块可以保证线程安全

b) synchronized支持锁的重入,即一个同步方法里面可以再调用类本身的同步方法(同一个线程可以获得同一把锁多次)

c) synchronized修饰普通方法,锁的对象是当前对象this;synchronized修饰静态方法,锁对象是当前类的字节码对象,两者不一样。

d) synchronized使用不当容易出现死锁问题

9.一个线程进入一个对象的同步方法后,其他线程可以访问该对象的其他方法吗?

a) 非同步方法可以访问

b) 静态同步方法和普通同步方法的锁对象不一样,所以可以互相访问

c) 当前同步方法中调用的wait()方法后其他线程可以访问其他同步方法

10.线程的两种实现方式

a) 1.继承Thread,重写run()方法

b) 2.实现Runnable接口,实现run()方法,然后创建线程时将实现Runnable的类的对象传给new Thread(Runnable obj)

11.启动线程是start()还是run()?为什么

a) 启动线程的方法是start()。start()是使一个线程进行就绪状态,代表此时可以由JVM调度执行。至于什么时候执行,取决于该线程何时得到满足运行的资源,才开始执行run()方法。start()间接调用的run()方法

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

a) Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已

b) Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果

c) Callable的优点:Callable+Future/FutureTask可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务

13.一个线程出现运行时异常会怎么样?

a) 如果异常没有被捕获或者抛出,那么线程将终止。发生运行时异常时线程会释放持有资源的锁

b) 异常被捕获线程会继续执行

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

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

15.wait()方法和notify()/notifyAll()方法在放弃对象的锁时有什么区别

a) wait()方法立即释放对象的锁,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象的锁

16.线程池的作用

a) 避免频繁地创建和销毁线程,达到线程对象的重用

b) 控制并发的数量,提升效率

17.怎么检测一个线程是否持有对象的锁?

a) Thread提供一个holdsLock(Object obj)方法,当前线程持有对象obj的锁时返回true;否则返回false

18.synchronized和ReentrantLock的区别

a) 前者是关键字,后者是类;所以ReentrantLock的拓展性比synchronized好

b) ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁

c) ReentrantLock可以获取各种锁的信息

d) ReentrantLock需要手动加锁、释放锁,比较灵活

19.ConcurrentHashMap的并发度是什么

a) ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作

20.ReadWriteLock是什么

a) ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

21.FutureTask是什么(常跟Callable一起使用)

a) FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作

22.Linux环境下查看那个线程使用CPU最长

a) 获取项目的pid(可以通过jps命令或者ps -ef | grep java查看项目所有线程的信息)

b) 根据项目的pid查看线程占用时间(命令top -H -p pid)

23.抛出InterruptedException异常终止线程(调用线程的interrupt()方法,在线程中抛出一个InterruptedException异常,然后捕获它)

24.写一个会导致死锁的例子

25.不可变对象对多线程有什么帮助

a) 不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率

26.什么是多线程的上下文切换

a) 多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程

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

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

b) 如果使用的是有界队列比如ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,会根据maximumPoolSize的值(最大线程数)增加线程数量,如果增加了线程数量还是处理不过来,则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy(丢弃任务并抛出RejectedExecutionException异常),其他三种策略:DiscardPolicy(丢弃任务,但不抛出异常)、DiscardOldestPolicy(丢弃队列最前面的任务,然后重新尝试执行该任务)、CallerRunsPolicy(被拒绝的任务添加到”线程池正在运行的线程”中去运行)

28.Java中用到的线程调度算法是什么

a) 抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行

29.Thread.sleep(0)的作用是什么

a) 由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。

30.什么是Java内存模型:Java内存模型定义了一种多线程访问Java内存的规范

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

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

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

d) happens-before,定义先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则,则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则,则这段代码一定是线程非安全的

31.乐观锁和悲观锁

a) 乐观锁:乐观锁就是用户修改数据时心态很乐观,不管别人修改不修改数据,我都不上锁,我修改的时候判断下数据有没有发生变化,没发生变化我就会更新成功,发生变化了就不会更新成功我再去重试之前的动作直到更新成功(java.util.concurrent.*下的线程安全类使用乐观锁)

b) 悲观锁:悲观锁就是用户修改数据时看起来很悲观,保守态度,担心别的用户会同时修改这条数据,所以每次修改时会提前把这条数据锁定起来,只有自己可修改(但别的用户可以读),等自己修改完了再释放锁(synchronized是悲观锁)

32.什么是CAS?

a) CAS:Compare and Swap,即比较交换

b) 利用CAS算法可以实现乐观锁,java.util.concurrent.*下的包就是使用CAS算法实现的

c) CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做

d) CAS(比较交换)是CPU指令级的操作,只有一步原子操作,所以非常快

33.什么是AQS?

a) AQS全称为AbstractQueuedSychronizer:抽象队列同步器

b) AQS就是整个Java并发包的核心,AQS实际上以双向队列的形式连接所有的Entry

34.单例模式的线程安全性

a) 懒汉单例模式:非线程安全的

b) 饿汉单例模式:线程安全的

c) 双检索单例模式:线程安全

35.Semaphore的作用

a) Semaphore就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized

36.Hashtable的size()方法中明明只有一条语句"return count",为什么还要做同步?

a) 同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程同时访问。所以,这样就有问题了,可能线程A在执行Hashtable的put方法添加数据,线程B则可以正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的,可能线程A添加了完了数据,但是没有对size++,线程B就已经读取size了,那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后,意味着线程B调用size()方法只有在线程A调用put方法完毕之后才可以调用,这样就保证了线程安全性

b) CPU执行代码,执行的不是Java代码,而是机器码(将Java代码翻译成字节码,再将字节码翻译成机器码):一条Java语句可能由几条机器码完成,所以有可能没执行完所有机器码线程就切换了

37.线程类的构造方法、静态块是被哪个线程调用?

a) 线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的

b) 例子:假设Thread2中new了Thread1,main函数中new了Thread2,那么:

viii. Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2自己调用的

ix. Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1自己调用的

38.同步方法和同步代码块那个更好?

a) 同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:正常情况下,同步的范围越小越好

39.高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?

a) 高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

b) 并发不高、任务执行时间长的业务要区分开看:

​ x. 假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务

​ xi. 假如是业务时间长集中在计算操作上,也就是计算密集型任务,线程池中的线程数设置得少一些,减少线程上下文的切换

c) 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,最后才考虑线程池的配置

40.并行和并发的区别

a) 并行是多个事件在同一时刻发生,在计算机则是多个处理器或者多核处理器同时处理多个任务;

b) 并发是多个事件按照CPU时间片交替发生

41.线程和进程的区别

a) 进程是资源分配的最小单位,线程是程序执行的最小单位

b) 进程有独立的地址空间;同一个进程下的线程使用相同的地址空间

c) 线程的切换比进程的切换开销小;线程的创建比进程的创建开销小

d) 线程间的通讯更加方便,因为一个进程下的线程共享资源

42.ThreadLocal是什么?使用场景?

a) ThreadLocal是本地线程副本工具类,存放一些当前线程的共享变量

b) 可以将数据库连接connection存到ThreadLocal中,从而实现线程的事务管理

43.synchronized和volatile的区别是什么?

a) volatile用来修饰变量;synchronized可以修饰方法、代码块、类

b) volatile修饰的变量可以保证修改变量时,所有的引用都会得到同样的值,但不能保证原子性

c) synchronized可以保证变量的修改可见性和原子性

d) volatile不会造成阻塞,synchronized可能会造成阻塞或死锁

44.如何防止死锁?

a) 锁使用tryLock设置一个超时时间,超时自动退出

b) 减少同步的代码

c) 使用java.util.concurrent并发类代替自己手动写锁

d) 降低锁的使用粒度,不要多个功能使用同一把锁

45.synchronized的底层实现原理

a) synchronized是由一对monitorenter和monitorexit指令实现的,monitor对象是同步的基本实现单元;monitor有三种实现:偏向锁、轻量级锁和重量级锁

46.synchronized和Lock的区别

a) synchronized可以给类、方法、代码块加锁;Lock只能给代码块加锁

b) synchronized不需要手动获取锁和释放锁;Lock需要手动加锁和释放锁,如果没有释放锁就会造成死锁

47.atomic的原理

a) atomic主要利用CAS和volatile、native方法保证可见性、原子操作,避免synchronized的高开销,效率比较高

48.volatile关键字的作用

a) volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据

b) 为了获取更好的性能JVM可能会对指令进行重排序(在多线程下可能会出现问题), volatile则会对禁止语义重排序

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 、 1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READmE.文件(md如有),本项目仅用作交流学习参考,请切勿用于商业用途。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值