java面试题 并发_java并发面试题(基础)

本文整理了常见的Java并发面试题,希望对大家面试有所帮助,欢迎大家互相交流。

多线程

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

可以使用Runnable,Callable,Thread或者线程池。

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

对于本问题,我认为准确的说法是:停止一个线程的最佳方法是让它执行完毕,没有办法立即停止一个线程,但你可以控制何时或什么条件下让他执行完毕。

通过条件变量控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。为保证线程间的即时通信,需要使用使用volatile关键字或锁,确保读线程与写线程间变量状态一致。下面给一个最佳模板:

1

2

3

4

5

6

7

8

9

10

11

12public class BestPractice extends Thread {

private volatile boolean finished = false; // ① volatile条件变量

public void () {

finished = true; // ② 发出停止信号

}

@Override

public void () {

while (!finished) { // ③ 检测条件变量

// do dirty work // ④业务代码

}

}

}

当④处的代码阻塞于wait()或sleep()时,线程不能立刻检测到条件变量。因此②处的代码最好同时调用interrupt()方法。

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

notify是随机唤醒一个等待某个资源的线程,进入就绪队列等待CPU的调度,notifyAll是唤醒所有的,进入就绪队列等待CPU调度

4、sleep()和 wait()有什么区别?

sleep是Thread类的方法,wait是Object类中定义的方法。sleep()保留对象锁,仍然占有该锁,wait()释放对象锁。调用wait后,需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。

%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81.jpg

5、什么是Daemon线程?它有什么意义?

用户线程:就是我们平时创建的普通线程。

守护线程:主要是用来服务用户线程。

那么如何来区分这两种线程呢?

其实在JDK的文档中已经说明的很清楚了:The Java Virtual Machine exits when the only threads running are all

daemon threads.

即:

当线程只剩下守护线程的时候,JVM就会退出。但是如果还有其他的任意一个用户线程还在,JVM就不会退出。

守护线程是后台线程,它通常属于低优先级的线程,守护用户线程,通常做一些垃圾清理的工作,比如GC。

6、java如何实现多线程之间的通讯和协作?

利用同步和互斥来解决多线程之间的通讯和协作;可以说资源互斥、协调竞争是要解决的因,而同步是竞争协调的果。

(1)通过synchronized/notify/notifyAll来实现线程之间的通信。

(2)利用了Java5中提供的Lock/Condition来实现线程之间的相互通信。

(3)使用信号量,如:CyclicBarrier/Semaphore/Countdownbatch。

怎么解决多线程计算的结果统计?

可以用join()以及Future/FutureTask来解决。join()的功能是使异步执行的线程变成同步执行;使用join()后,直到这个线程退出,进程才会往下执行。

1、什么是可重入锁(ReentrantLock)?

ReentrantLock 类实现了 Lock ,它拥有与synchronized相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。

简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的synchronized块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

2、当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?

分几种情况:

(1)其他方法前是否加了synchronized关键字,如果没加,则能。

(2)如果这个方法内部调用了wait,则可以进入其他synchronized方法。

(3)如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。

(4)如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。

当一个线程进入一个对象的synchronized()方法后,其他线程是否可以进入此对象的其他方法取决于方法本身,如果该方法是非synchronized()方法,那么是可以访问的,如果是synchronized()方法,那么不能访问。

3、synchronized和java.util.concurrent.locks.Lock的异同?

(1)Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等

(2)synchronized是Java语言层面的,是内置的关键字;Lock则是JDK5中出现的一个包,在使用时,synchronized同步的代码块可以由JVM自动释放;Lock 需要进程员在finally块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。

4、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

并发框架

1、SynchronizedMap和ConcurrentHashMap有什么区别?

Collections.synchronizedMap()与ConcurrentHashMap主要区别是:Collections.synchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。

还有一个区别是:ConcurrentHashMap从类的命名就能看出,它必然是个HashMap。而Collections.synchronizedMap()可以接收任意Map实例,实现Map的同步。

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

CopyOnWriteArrayList类最大的特点就是,在对其实例进行修改操作(add/remove等)会新建一个数据并修改,修改完毕之后,再将原来的引用指向新的数组。这样,修改过程没有修改原来的数组。也就没有了ConcurrentModificationException错误。

适用于多读少写场景。

线程安全

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

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

servlet不是线程安全的,如果多个HTTP请求请求的是同一个Servlet,那么着两个HTTP请求对应的线程将并发调用Servlet的service()方法。

这时候,如果在Servlet中定义了实例变量或静态变量,那么可能会发生线程安全问题(因为所有的线程都可能使用这些变量)。

2、同步有几种实现方法?

(1)同步方法:即有synchronized关键字修饰的方法。

(2)同步代码块:即有synchronized关键字修饰的语句块。

(3)使用特殊域变量(volatile)实现线程同步

a.volatile关键字为域变量的访问提供了一种免锁机制

b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新

c.因此每次使用该域就要重新计算,而不是使用寄存器中的值

d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

(4)使用重入锁实现线程同步

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。

ReenreantLock类的常用方法有:

ReentrantLock() : 创建一个ReentrantLock实例

lock() : 获得锁

unlock() : 释放锁

(5)使用局部变量实现线程同步 :如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

3、volatile有什么用?能否用一句话说明下volatile的应用场景?

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。

您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

a. 对变量的写操作不依赖于当前值。

b. 该变量没有包含在具有其他变量的不变式中。

4、请说明下java的内存模型及其工作流程。

Java把内存划分成两种:一种是栈内存,一种是堆内存。

栈内存:存放对象:函数中基本类型的变量和对象的引用变量、静态类方法 ;特点:栈有一个很重要的特殊性,就是存在栈中的数据可以共享。

堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些对象成员变量的拷贝,线程对所有对象成员变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

(1) 获取对象监视器的锁(lock)

(2) 清空工作内存数据, 从主存复制对象成员变量到当前工作内存, 即同步数据 (read and load)

(3) 执行代码,改变共享变量值 (use and assign)

(4) 将工作内存数据刷回主存 (store and write)

(5) 释放对象监视器的锁 (unlock)

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

编译器或运行时环境为了优化进程性能而采取的对命令进行重新排序执行的一种手段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值