文章目录
-
- 1.在 Java 中守护线程和用户线程的区别?
- 2.线程与进程的区别
- 3.什么是多线程中的上下文切换
- 4.死锁与活锁的区别,死锁与饥饿的区别?
- 5. synchronized 底层实现原理
- 6.什么是线程组,为什么在 Java 中不推荐使用?
- 7.什么是 Executors 框架?为什么使用 Executor 框架?
- 8.在 Java 中 Executor 和 Executors 的区别?
- 9.什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)?
- 10. Java Concurrency API 中的 Lock 接口(Lock interface)是什么?对比 synchronized 它有什么优势?
- 11. 什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?
- 12. 什么是 Callable 和 Future?
- 13. 什么是 FutureTask?
- 14. 什么是竞争条件?
- 15. 启动线程的方式有几种?
- 16. 为什么我们调用 start()方法时会执行 run()方法,为什么我们不能直接调用 run()方法?
- 17. 执行两次 start 方法可以吗?
- 18. 在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?
- 19. 什么是不可变对象,它对写并发应用有什么帮助?
- 20. notify()和 notifyAll()有什么区别?
- 21. 什么是可重入锁(ReentrantLock)?谈谈它的实现。
- 22. 当一个线程进入某个对象的一个 synchronized的实例方法后,其它线程是否可进入此对象的其它方法?
- 23. 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
- 24. 什么是 CAS 操作,缺点是什么?
- 25. SynchronizedMap 和 ConcurrentHashMap 有什么区别?
- 26. 写时复制容器可以用于什么应用场景?
- 27. volatile 有什么用?能否用一句话说明下volatile 的应用场景?
- 28. 为什么代码会重排序?
- 29. 在 Java 中 wait 和 sleep 方法的不同?
- 30. 一个线程运行时发生异常会怎样?
- 31. 为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?
- 32. 什么是 ThreadLocal 变量?
- 33. Java 中 interrupted 和 isInterrupted 方法的区别?
- 34. 为什么wait和notify方法要在同步块中调用?
- 35. 为什么你应该在循环中检查等待条件?
- 36. 怎么检测一个线程是否拥有锁?
- 37. 你如何在 Java 中获取线程堆栈信息?
- 38. Java 线程池中 submit() 和 execute()方法有什么区别?
- 39. 你对线程优先级的理解是什么?
- 40. 你如何确保 main()方法所在的线程是 Java 程序最后结束的线程?
- 41. 为什么 Thread 类的 sleep()和 yield ()方法是静态的?
- 42. 现在有 T1、T2、T3 三个线程,你怎样保证 T2 在T1 执行完后执行,T3 在 T2 执行完后执行?
- 43. 用 Java 写代码来解决生产者——消费者问题。
- 44. Java 中如何停止一个线程?
- 45. JVM中哪个参数是用来控制线程的栈堆栈大小的
-
- 46. 如果同步块内的线程抛出异常,锁会释放吗?
- 47. 单例模式的双重检查实现是什么?为什么并不安全?如何在 Java 中创建线程安全的 Singleton?
- 48. 请概述线程池的创建参数,怎么样合理配置一个线程池的参数?
- 49. 请概述锁的公平和非公平,JDK 内部是如何实现的。
- 50. 请概述 AQS
- 51. 请概述 volatile
- 52. HashMap 和 HashTable 有什么区别?
- 53. Java 中的另一个线程安全的与 HashMap 极其类似的类是什么?同样是线程安全,它与HashTable 在线程同步上有什么不同?
- 54. HashMap & ConcurrentHashMap 的区别?
- 55. 为什么 ConcurrentHashMap 比 HashTable 效率要高?
- 56. 针对 ConcurrentHashMap 锁机制具体分析(JDK 1.7 VS JDK 1.8)?
- 57. ConcurrentHashMap 在 JDK 1.8 中,为什么要使用内置锁 synchronized 来代替重入锁ReentrantLock?
- 58. ConcurrentHashMap 简单介绍?
- 59. ConcurrentHashMap 的并发度是什么?
- 60. 什么是锁消除和锁粗化?
- 61. 除了 ReetrantLock, JUC 下还有哪些并发工具?
- 62. 什么是 Java 的内存模型,Java 中各个线程是怎么彼此看到对方的变量的?
|
|
1.在 Java 中守护线程和用户线程的区别?
Java 中的线程分为两种:守护线程(Daemon)和用户线程(User)。
任何线程都可以设置为守护线程和用户线程,通过方法 Thread.setDaemon(bool on);
true 则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在
Thread.start()之前调用,否则运行时会抛出异常。
两者的区别:
唯一的区别是判断虚拟机(JVM)何时离开,Daemon 是为其他线程提供服务,如果全部
的 User Thread 已经结束,Daemon 没有可服务的线程,JVM 关闭。
扩展:Thread Dump 打印出来的线程信息,含有 daemon 字样的线程即为守护进程
2.线程与进程的区别
进程是操作系统配资源的最小单元,线程是操作系统调度的最小单元。
一个程序至少有一个进程,一个进程至少有一个线程。
3.什么是多线程中的上下文切换
多线程会共同使用一组计算机上的 CPU,而线程数大于给程序分配的 CPU 数量时,为了
让各个线程都有执行的机会,就需要轮转使用 CPU。不同的线程切换使用 CPU 发生的切换数
据等就是上下文切换。
4.死锁与活锁的区别,死锁与饥饿的区别?
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一
种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生死锁的必要条件:
互斥条件:所谓互斥就是进程在某一时间内独占资源。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失
败,尝试,失败。
活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处
于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的
状态。
5. synchronized 底层实现原理
synchronized (this)原理:涉及两条指令:monitorenter,monitorexit;再说同
步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令 monitorenter 和
monitorexit 来实现,相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。
JVM 就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将会检查方法
的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取 monitor,获
取成功之后才能执行方法体,方法执行完后再释放 monitor。在方法执行期间,其他任何线
程都无法再获得同一个 monitor 对象。
注意,这个问题可能会接着追问,Java 对象头信息,偏向锁,轻量锁,重量级锁及其
他们相互间转化。
6.什么是线程组,为什么在 Java 中不推荐使用?
ThreadGroup 类,可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可
以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式。
1.线程组 ThreadGroup 对象中比较有用的方法是 stop、resume、suspend 等方法,由
于这几个方法会导致线程的安全问题(主要是死锁问题),已经被官方废弃掉了,所以线程
组本身的应用价值就大打折扣了。
2.线程组 ThreadGroup 不是线程安全的,这在使用过程中获取的信息并不全是及时有
效的,这就降低了它的统计使用价值。
7.什么是 Executors 框架?为什么使用 Executor 框架?
Executor 框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。
每次执行任务创建线程 new Thread()比较消耗性能,创建一个线程是比较耗时、耗
资源的。
调用 new Thread()创建的线程缺乏管理,而且可以无限制的创建,线程之间的相互
竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统
资源。
接使用 new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期
执行、线程中断等都不便实现。
8.在 Java 中 Executor 和 Executors 的区别?
Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需
求。
Executor 接口对象能执行我们的线程任务。
ExecutorService 接口继承了 Executor 接口并进行了扩展,提供了更多的方法我们能
获得任务执行的状态并且可以获取任务的返回值。
使用 ThreadPoolExecutor 可以创建自定义线程池。
9.什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)?
原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。
处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。
在Java中可以通过锁和循环CAS的方式来实现原子操作。CAS操作——Compare & Set,
或 是 Compare & Swap ,现在几乎所有的 CPU 指令都支持 CAS 的原子操作。
Java.util.concurrent.atomic 下提供了大量的原子操作类,比如原子类:AtomicBoolean,
AtomicInteger, AtomicLong ,AtomicReference ,原子数组: AtomicIntegerArray,
AtomicLongArray,AtomicReferenceArray ,原子属性更新器:AtomicLongFieldUpdater,
AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
10. Java Concurrency API 中的 Lock 接口(Lock interface)是什么?对比 synchronized 它有什么优势?
Lock 接口比同步方法和同步块提供了更具扩展性的锁操作。
他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件
对象。
它的优势有:可以使锁更公平,可以使线程在等待锁的时候响应中断,可以让线程尝
试获取锁,并在无法获取锁的时候立即返回或者等待一段时间,可以在不同的范围,以不同
的顺序获取和释放锁。
整体上来说 Lock 是 synchronized 的扩展版,Lock 提供了无条件的、可轮询的(tryLock
方法)、定时的(tryLock 带参方法)、可中断的(lockInterruptibly)、可多条件队列
的(newCondition 方法)锁操作。另外 Lock 的实现类基本都支持非公平锁(默认)和公平
锁,synchronized 只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
11. 什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列
满时,存储元素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者
是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿
元素。
JDK7 提供了 7 个阻塞队列。在实现上,主要是利用了 Condition 和 Lock 的等待通知模
式。
12. 什么是 Callable 和 Future?
Callable 接口类似于 Runnable,从名字就可以看出来了,但是 Runnable 不会返回结
果,并且无法抛出返回结果的异常,而 Callable 功能更强大一些,被线程执行后,可以返
回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。
可以认为是带有回调的 Runnable。
Future 接口表示异步任务,是还没有完成的任务给出的未来结果。所以说 Callable
用于产生结果,Future 用于获取结果。
13. 什么是 FutureTask?
在 Java 并发程序中 FutureTask 表示一个可以取消的异步运算。它有启动和取消运算、
查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算
尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的
对象进行包装,由于 FutureTask 也是调用了 Runnable 接口所以它可以提交给 Executor 来
执行。
14. 什么是竞争条件?
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序
时,则我们认为这发生了竞争条件(race condition)。
15. 启动线程的方式有几种?
两种,启动线程的方式有:
1、X extends Thread;,然后 X.start
2、X implements Runnable;然后交给 Thread 运行
16. 为什么我们调用 start()方法时会执行 run()方法,为什么我们不能直接调用 run()方法?
当你调用 start()方法时你将创建新的线程,