Java多线程面试题(二)


前言

最新的 Java 面试题,技术栈涉及 Java 基础、集合、多线程、Mysql、分布式、Spring全家桶、MyBatis、Dubbo、缓存、消息队列、Linux…等等,会持续更新。

如果对老铁有帮助,帮忙免费点个赞,谢谢你的发财手!

1、Java 中有几种方法可以实现一个线程?

1)继承Thread类创建线程,重写run() 方法
2)实现Runnable接口创建线程,重写run() 方法
3)实现Callable接口创建线程,重写call() 方法,常和FutureTask一起使用
4)使用线程池,例如用Executor框架
5)Spring @Async异步注解 结合线程池 。

2、如何停止一个正在运行的线程?

使用Thread 提供的 interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常。

3、notify()和 notifyAll()有什么区别?

当一个线程进入 wait 之后,就必须等其他线程 notify/notifyall,使用 notifyall,可以唤醒所有处于 wait 状态的线程,使其重新进入锁的争夺队列中,而 notify 只能随机唤醒一个。
如果没把握,建议 notifyAll,防止 notify 因为信号丢失而造成程序异常。

4、多线程之间如何通讯?

  • 1.共享变量:通过共享内存中的变量,在不同的线程之间进行数据传递,这种方式需要使用volatile或者synchronized关键字保证可见性和原子性;
    1. wait()和notify()/notifyAll(),这种方式必须在同步块中使用,而且只有持有锁的线程才能调用notify()/notifyAll()方法;
    1. 管道流:通过管道输入流(PipedInputStream)和管道输出流(PipedOutputStream)来实现多个线程之间的通信。这种方式适用于需要高效传输较小数据量的场景;
    1. Future和Callable:通过Future和Callable可以让一个线程在另一个线程执行完毕后获取其执行结果。

5、多线程之间如何协作?

  • 1、join()方法:当一个线程需要等待另一个线程执行完毕后再继续执行时,可以调用join()方法。
  • 2、CountDownLatch类:该类可以让一个或多个线程等待其他线程执行完毕后再继续执行。可以通过CountDownLatch的构造函数设置计数器,每个线程执行完自己的任务后调用countDown()方法使计数器减一,当计数器为0时,等待的线程就会被唤醒。
  • 3、Semaphore类:该类可以限制同时执行的线程数量,可以通过Semaphore的构造函数设置许可证数量,线程在执行前必须先获得许可证,执行完后释放许可证以供其他线程使用。
  • 4、CyclicBarrier类:该类可以让多个线程互相等待,直到所有线程都执行到某个屏障点再继续执行。可以通过CyclicBarrier的构造函数设置参与线程的数量,并且每个线程到达屏障点后调用await()方法等待其他线程到达,可以调用signal()或signalAll()方法来唤醒等待的线程

6、当一个线程进入某个对象的一个同步方法后,其它线程是否可进入此对象的其它方法?

如果其他方法没有 synchronized 的话,其他线程是可以进入的。
所以要开放一个线程安全的对象时,得保证每个方法都是线程安全的。

7、乐观锁和悲观锁的理解?

  • 乐观锁:是指乐观的认为自己在操作数据时候,别人不会同时修改数据,所有乐观锁不会对数据加锁。
    乐观锁的实现方式主要有两种:CAS机制和版本号机制,常用的是版本号机制,简单来说就是在数据中增加一个字段version,表示该数据的版本号,每当数据被修改,版本号加1。当某个线程修改数据时,会先判断当前版本号与之前查询出来的版本号是否一致,如果一致才进行操作。
  • 悲观锁:是指悲观的认为自己在操作数据时候,别人也会同时修改数据,所以悲观锁会对数据加锁,悲观锁的实现方式主要也有两种:synchronized关键字和ReentrantLock接口,另外对于MySQL数据库,其实利用了MySQL的排他锁,行锁必须有索引才能实现,否则就是表锁了。

8、SynchronizedMap 、HashTable和 ConcurrentHashMap 有什么区别?

  • SynchronizedMap和HashTable 一次锁住整张表来保证线程安全,所以每次只能有一个线程来访问 map。
  • ConcurrentHashMap 使用分段锁来保证在多线程下的性能。
  • ConcurrentHashMap 中则是一次锁住一个桶。ConcurrentHashMap 默认将hash 表分为 16 个桶,诸如 get,put,remove 等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有 16 个写线程执行,并发性能的提升是显而易见的。

9、CopyOnWriteArrayList 可以用于什么应用场景?

它相当于线程安全的ArrayList,和ArrayList一样,它是个可变数组,适合使用在读取操作远远大于写操作的场景中,比如缓存。
当有写入操作的时候,需要复制整个基础数组,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行,但最终会达到一致性(将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了)
比如List list = new CopyOnWriteArrayList();

10、什么叫线程安全?servlet 是线程安全吗?

线程安全是指每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期是一样的,这就是线程安全。
Servlet 不是线程安全的,servlet 是单实例多线程的,当多个线程同时访问同一个方法,是不能保证共享变量的线程安全性的。
Struts2 的 action 是多实例多线程的,是线程安全的,每个请求过来都会 new 一个新的 action 分配给这个请求,请求完成后销毁。
SpringMVC 的 Controller 是线程安全的吗?不是的,和 Servlet 类似的处理流程。
Struts2 好处是不用考虑线程安全问题;Servlet 和 SpringMVC 需要考虑线程安全问题,但是性能可以提升不用处理太多的 gc,可以使用 ThreadLocal 来处理多线程的问题。

11、volatile 有什么用?

volatile是一个类型修饰符,可以用来修饰被不同线程访问和修改的变量,被volatile修饰的变量,系统每次用到它时都是直接从内存当中读取,而不会读取缓存,所以线程在任何时候所读取的变量值都是相同的。

12、为什么代码会重排序?

在执行程序时,为了提高性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,它需要满足两个条件:
1、在单线程环境下不能改变程序运行的结果;存在数据依赖关系的不允许重序
2、需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。

13、在Java 中 wait 和 sleep 方法的不同?

  • 1、sleep()是Thread类中的方法,wait()是Object类中的方法
  • 2、调用sleep方法时并不会释放锁资源,而调用wait方法时,线程会释放锁资源(但都会释放CPU)。
  • 3、sleep方法可以在任何地方使用,而wait方法只能在同步方法或者同步代码块里面(synchronized)使用。
  • 4、sleep方法必须捕获异常,而wait方法不需要捕获异常。

14、一个线程运行时发生异常会怎样?

出现Error或者异常没有被捕获,程序会停止运行。
如果出现异常被捕获或抛出,则程序继续运行。

15、Java 中 notify 和 notifyAll 有什么区别?

notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池·。而 notifyAll()唤醒对象的等待池中的所有线程,进入锁池。

16、为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?

一个很明显的原因是 JAVA 提供的锁是对象级的,而不是线程级的。每个对象都有锁,通过线程获得。由于 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object 类中,因为锁属于对象。

17、Java 中 interrupted 和 isInterrupted 方法的区别?

  • Interrupt:
    interrupt 方法用于中断线程,调用该方法的线程的状态为将被置为”中断”状态。
    注意:线程中断仅仅是指线程的中断状态位,正在进行的线程不会停止。
  • Interrupted:
    查询当前线程的中断状态,并且清除原状态。如果一个线程被中断了,第一次调用 interrupted 则返回 true,第二次和后面的就返回 false 了。
    isInterrupted:
    仅仅是查询当前线程的中断状态 。

18、什么是 ThreadLocal 变量?

ThreadLocal意为线程本地变量,用于解决多线程并发请求访问共享变量的问题。
ThreadLocal为每个线程都创建了一个实例副本,而其它 Thread 不可访问,实现线程之间的隔离性,所以就不存在多线程共享的问题。当一个线程结束时,它所使用的实例副本都会被回收。
static ThreadLocal threadLocal = new ThreadLocal<>();

19、为什么 wait 和 notify 方法要在同步块中调用?

Java API 强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException 异常。还有一个原因是为了避免 wait 和 notify 之间产生竞态条件。

20、怎么检测一个线程是否拥有锁?

在 java.lang.Thread 中有一个方法叫 holdsLock(),只有当前线程持有指定对象的锁时,方法才返回 true;
它是一个由native修饰的非java代码实现的方法。
Thread.holdsLock(object);

总结

都已经看到这里啦,赶紧收藏起来,祝您工作顺心,生活愉快!

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值