线程池的作用
线程是一种有限的宝贵的系统资源,创建线程需要很多时间和系统资源,需要对线程进行回收利用,降低对系统资源的消耗,提升服务器的执行效率。
几种常见的线程池
顶层接口:Executor
方法:execute(Runnable) 将任务交给线程池执行。
子接口:ExecutorService
方法:
1.shutdown() 停止线程池,会等待线程执行完。
2.shutdownNow() 停止线程池,会终止线程执行。
ExecutorService的子接口:ScheduledExecutorService
常用的方法:
在固定的延迟时间后执行
scheduleWithFixedDelay(执行的Runnable任务,初始的延迟,延迟,时间单位)
在固定的周期执行:
scheduleAtFixedRate(执行的Runnable任务,初始的延迟,周期,时间单位)
工具类:Executors 提供了几个静态方法,帮助方便的创建线程池。
线程池的7个参数
线程池的实现类:ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
优化配置的几种方式
1.核心线程数
和cpu核心数以及任务数量和执行时间相关。
核心线程数 = cpu核心数 * n ,n >= 1 上限和任务数以及执行时间相关。
2.最大线程数
最大线程数和核心线程数相同,减少系统创建线程和销毁线程的时间和资源,提高性能。
3.keepAliveTime
如果最大线程数和核心线程数相同,可以设置为0;
否则可以尽量大一点,减少系统创建线程和销毁线程的时间和资源。
4.BlockingQueue
设置为LinkedBlockQueue,任务经常添加和删除时,性能更高。
设置为SynchorousQueue,能处理的任务数更多,吞吐量更高(每秒处理请求数)。
线程池的原理
线程池总的执行流程
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
线程池的线程是如何回收?
步骤:
1.通过线程池的execute执行任务。
2.通过调用线程池的addWorker方法添加工作线程。
3.同时把Runnable执行任务添加到workQueue中。
4.创建Worker添加到HashSet中,Worker中包含线程。
5.启动Worker中的线程。
6.线程调用runWorker方法。
7.runWorker中判断任务不为空就执行任务。
8. 任务如果为空,就调用getTask()方法取任务。
9.循环不断调用workerQueue阻塞队列的take()方法。
10.任务队列为空就自动阻塞当前线程,直到任务队列不为空唤醒线程去执行任务。
线程相关的工具类
CountDownLatch类
门栓,有时候一个线程的执行需要等待其它线程执行完后再执行。
创建:
new CountDownLatch(倒数的数字)
常用的API:
void countDown() //倒数一次,数字减一
void await() //阻塞当前线程,当倒数为0就唤醒
简单的使用案例
/**
* CountDownLatch类
*/
public class CutDownLatchDemo {
public static void main(String[] args) {
//创建倒数对象
CountDownLatch countDownLatch = new CountDownLatch(3);
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"等待别的线程执行完");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}).start();
for (int i = 0; i < 3; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+"执行"+j);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//倒数一次
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName()+"执行完毕,count="+countDownLatch.getCount());
}).start();
}
}
}
Semaphore类
信号量,控制启动线程的数量。
创建:
new Semaphore(最大数量)
常用的API:
aquired() //请求一个信号量
release() //释放一个信号量
简单使用案例
/**
* Semaphore类
*/
public class SemphoreDemo {
public static void main(String[] args) {
//创建信号量
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 100; i++) {
new Thread(()->{
//请求信号量
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开始执行");
try {
Thread.sleep(500);
//释放信号量
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}