(一)问:Java线程池的实现原理和使用
线程池即存放和管理线程的一个池子
(1)复用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
(2)能够有效的控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
(3)能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
当向线程池提交任务时:
(1)线程池判断核心线程池里的线程是否都在执行任务(核心线程池是不是已经满载状态)。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
(2)线程池判断工作队列是否已经满(有界队列)。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
(3)线程池判断线程池的线程(临时线程)是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务(抛出异常、交给提交任务的线程、等待线程、剃除第一个线程加入等等)
线程池构造函数:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runable> workQueue, ThreadFactory threadFactory, RejectedExcutionHandler handler)
corePoolSize:核心线程数
maximumPoolSize: 总线程数
keepAliveTime:线程超时时间,设置超时时间,如果线程空闲则会被回收。 unit为超时时间的单位。
workQueue:它是一个等待执行的任务队列(工作队列),如果核心线程没有空闲的了,新来的任务就会被放到这个等待队列中
threadFactory:线程工厂,用于生成临时线程
handler:指定取舍策略,如果任何无法在核心线程、工作队列、临时线程中都得不到执行,则需要做取舍的。
(二)问:线程同步有哪些方式
答:使用多线程时,保证数据的唯一性和准确性,所以要使用线程同步,线程同步有以下五种方式:
(1)synchronized关键字(对方法或代码块),如果方法是静态代码,会锁住整个类。通常只需要使用synchronized(this){} 代码块,一般不需要锁住整个方法或类
(2)使用重入锁ReentrantLock(),需要手动lock() 和 unlock()
(3)使用局部变量ThreadLocal,如果使用ThreadLocal管理变量,那么每一个使用该变量的线程都会获得该变量的副本,这样每一个线程可以随意修改自己的副本,不会对其他线程产生影响。
(4)使用阻塞队列LinkedBlockingQueue<E e>,put(E e) 队列末尾添加元素,如果队列满则阻塞, take() 返回队首并移除,如果队空则阻塞。
(5)使用特殊域变量(volatile)实现线程同步
(三)问:什么是死锁?如何解决或预防产生死锁
答:死锁,就是指两个或两个以上的线程/进程在执行的过程中,因争夺资源而造成的一种相互等到的现象。例如:线程A持有锁A, 线程B持有锁B,此时线程A请求获取锁B,线程B请求获取锁A,就造成了相互等待的结果,也就是死锁。
解决方案:
1.避免在同步代码块中调用外部的同步方法。
2.在嵌套多层synchronized的代码块中,通过对象唯一值给对象锁进行排序,例如使用对象的hashCode值进行排序,使得每次获取锁的顺序是一致的。
网友评论