面试题-线程
主页
标签
文章
### JAVA中线程的实现方式? 1. 继承Thread类,重写run()方法 2. 实现Runnable接口,重写run()方法 3. 实现Callable,重写call()方法,配合FutureTask 4. 使用线程池构建线程 **追其底层,其实都是实现Runnable接口的run()方法** ### 线程有几种状态? 从系统层面讲,有5种:NEW,READY,RUNNING,WAITING,TERMINATED 从JAVA层面讲,Thread类中有一个枚举类State,有6种状态:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED - NEW:Thread对象被创建出来了,但是还没有执行start方法。 - RUNNABLE:Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度) - BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程 - BLOCKED:synchronized没有拿到同步锁,被阻塞的情况 - WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒 - TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒 - TERMINATED:run方法执行完毕,线程生命周期到头了 ### 线程池有几种状态?每种状态分别表示什么? 在ThreadPoolExecutor中提供了5种状态:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED - RUNNING:线程池新建或调用executor方法后,处于运行状态时,能够接收新任务 - SHUTDOWN:调用shutdown()方法后,线程池编程SHUTDOWN状态,此时线程池不再接收新的任务,但会执行已提交的等待任务队列种的任务 - STOP:调用shutdownNow()方法后,线程池的状态会编程STOP,此时线程池不再接收新的任务,并且会中断正在处理中的任务 - TIDYING:整理状态,中间状态不做任何处理 - TERMINATED:线程池内容的所有线程都已经终止时,线程池进入TERMINATED状态 ### 如何停止线程? 1. stop方法(方法已过时,不使用) 2. 使用共享变量(很少会使用,其实就是定义一个flag,用while不停循环,直到flag变更,线程结束) 3. 使用interrupt()方法(线程内部有一个终端标记位,和共享变量相似) ### Java中sleep和wait的区别? 1. sleep属于Thread类中的static方法,wait属于Object类的方法(代表任意类都可以使用它) 2. sleep属于TIMED_WAITING状态,自动被唤醒,wait属于WAITING状态,需要手动唤醒 3. sleep方法在持有锁时执行不会释放锁资源,wait在执行后会释放锁资源 4. sleep无论是否持有锁,都可以执行,wait方法只能在持有锁时执行 ### 并发编程的三大特性? 1. 原子性:一个线程在执行时,另一个线程不会影响到他 - synchronized - CAS - Lock锁 2. 可见性:线程工作时,从主内存拿到数据后,会存到CPU的三级缓存中,但是CPU都是多核的,每个线程工作的缓存都是不同的,就会数据不一致 - volatile - synchronized - Lock - final 3. 有序性:可以禁止编译时和CPU的重排 - as-if-serial - happens-before - volatile ### 什么是CAS?有什么优缺点? compare and swap也就是比较和交换,他是一条CPU的并发原语。他在替换内存的某个位置的值时,首先查看内存中的值与预期值是否一致,如果一致,执行替换操作。 - 优点:线程不会挂起 - 缺点:ABA问题(用版本号解决,AtomicStampeReference),自旋时间过长(指定自旋次数),只能对一个变量保证原子性,不能对多行代码保证原子性 ### @Contended注解有什么用? 解决缓存行同步带来的性能问题 CPU在操作主内存变量前,会将主内存数据存到CPU缓存中,CPU缓存L1是以缓存行为单位存储数据的,一般默认大小64字节,如果某个变量不足64字节,再有另一个变量和他一起存在缓存行中,当另一个变量改变时,这个缓存行会把两个变量一起改变,这样会有效率问题 @Contented注解就是将当前类中的数据,独占一个缓存行,缓存行空的部分会填充一些没有意义的数据将缓存行填满 ### Java中的四种引用类型 Java中的使用引用类型分别是强,软,弱,虚。 - 强引用:new出来的都是强引用,它不可能被垃圾回收机制回收。是造成Java内存泄漏的主要原因之一 - 软引用:用SoftReference,当系统内存足够时不会被回收,当系统内存不足时它会被回收 - 弱引用:垃圾回收机制一运行,就会回收该对象占用的内存。ThreadLocal就是基于弱引用解决内存泄漏问题 - 虚引用:不能单独使用,必须和引用队列联合使用,主要用作跟踪对象被垃圾回收的状态 ### ThreadLocal的内存泄露问题? 每个ThreadLocal都有一个ThreadLocalMap存储数据(它是基于Entry[]实现的),这里ThreadLocal本身作为了key,保存着它的value,但是这是一个弱引用,如果ThreadLocal引用丢失,就会GC回收,如果此时线程还没有被回收,就会导致内存泄漏,value无法被回收,也无法被获取 - 解决方法:在使用完毕ThreadLocal对象后,及时的调用remove方法,移除Entry ### Java中锁的分类 - 可重入锁、不可重入锁:synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁 - 可重入锁:当前线程获取到A锁,再次获取A锁可以直接拿到 - 不可重入锁:当前线程获取到A锁,再次获取A锁无法获取,必须等待自己释放锁再获取锁 - 乐观锁、悲观锁:synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁 - 悲观锁:获取不到锁时,当前线程挂起 - 乐观锁:获取不到锁时,可以再次让CPU调度尝试重新获取。Atomic类中,就是基于CAS乐观锁实现的 - 公平锁、非公平锁:ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁 - 公平锁:A拿到锁,B获取不到去排队,C来了看到A持有B排队,直接排到B后面等待获取锁 - 非公平锁:A拿到锁,B获取不到去排队,C来了先竞争一下锁,拿到了就插队,没拿到再去B后面排队 - 互斥锁、共享锁:synchronized、ReentrantLock是互斥锁,ReentrantReadWriteLock,有互斥锁也有共享锁 - 互斥锁:只有一个线程持有 - 共享锁:可以被多个线程同时持有 ### synchronized在JDK1.6中的优化 1. 锁消除:修饰的代码中,如果没有操作任何临界资源,JIT会触发锁消除,即便写了也不会有任何效果,不会反复竞争 2. 锁膨胀:如在for循环中频繁的获取释放资源,会有很大的消耗,JIT会做所膨胀优化,把锁向外膨胀到循坏的外层,这样只需要获取一次锁资源 3. 锁升级:锁分成了几种类别,逐渐将锁升级,不能降级 - 无锁、匿名偏向:当前对象没有作为锁存在 - 偏向锁:如果只有一个线程在频繁获取和释放,那么它来的时候只需要判断一下就可以获取到,如果出现了其他线程竞争,就会升级为轻量级锁 - 轻量级锁:频繁的以CAS的方式获取锁,自旋了一定次数没拿到,锁升级为重量级锁 - 重量级锁:传统的synchronized锁,拿不到就挂起 ### Sychronized和ReentrantLock有哪些不同? 核心区别: * ReentrantLock是个类,synchronized是关键字,当然都是在JVM层面实现互斥锁的方式 效率区别: * 如果竞争比较激烈,推荐ReentrantLock去实现,不存在锁升级概念。而synchronized是存在锁升级概念的,如果升级到重量级锁,是不存在锁降级的。 底层实现区别: * 实现原理是不一样,ReentrantLock基于AQS实现的,synchronized是基于ObjectMonitor 功能向的区别: * ReentrantLock的功能比synchronized更全面。 * ReentrantLock支持公平锁和非公平锁,synchronized是非公平锁 * ReentrantLock可以指定等待锁资源的时间。 ### JDK提供了哪些线程池 newFixedThreadPoor newSingleThreadExecutor newCachedThreadPool newScheduleThreadPool newWorkStealingPool
上一篇:
面试题-基础
下一篇:
Enum枚举类通过key获取value
Title
-
Artist
0:00