java 线程池

为什么要使用线程池

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。创建线程关闭线程花销是比较大的。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
(参考 https://www.zhihu.com/question/41134816/answer/1086805195)

线程池的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class UseThreadPool {
//工作线程
static class Worker implements Runnable
{

private String taskName;
private Random r = new Random();

public Worker(String taskName){
this.taskName = taskName;
}

public String getName() {
return taskName;
}

@Override
public void run(){
System.out.println(Thread.currentThread().getName()
+" process the task : " + taskName);
SleepTools.ms(r.nextInt(100)*5);
}
}

static class CallWorker implements Callable<String>{

private String taskName;
private Random r = new Random();

public CallWorker(String taskName){
this.taskName = taskName;
}

public String getName() {
return taskName;
}

@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName()
+" process the task : " + taskName);
return Thread.currentThread().getName()+":"+r.nextInt(100)*5;
}

}

public static void main(String[] args)
throws InterruptedException, ExecutionException
{

ExecutorService pool = new ThreadPoolExecutor(2,4,3,TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new ThreadPoolExecutor.DiscardOldestPolicy());
for(int i=0;i<6;i++) {
Worker worker = new Worker("worker_"+i);
pool.execute(worker);
}
for(int i=0;i<6;i++) {
CallWorker callWorker = new CallWorker("callWorker_"+i);
Future<String> result = pool.submit(callWorker);
System.out.println(result.get());
}
pool.shutdown();
}
}

线程池中几个参数的含义

1
2
3
4
5
6
7
8
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(corePoolSize,// 核心线程数  
maximumPoolSize, // 最大线程数
keepAliveTime, // 闲置线程存活时间
TimeUnit.MILLISECONDS,// 时间单位
new LinkedBlockingDeque<Runnable>(),// 线程队列
Executors.defaultThreadFactory(),// 线程工厂
new AbortPolicy()// 队列已满,而且当前线程数已经超过最大线程数时的异常处理策略
);

四种拒绝策略

CallerRunsPolicy : 当线程池和队列都满时,任务将会被任务的调用方线程执行,如果线程池关闭,那么任务将会被抛弃
AbortPolicy :当线程池和队列都满时,再有任务进来直接抛出RejectedExecutionException异常
DiscardPolicy: 当线程池和队列都满时,再有任务进来,默默的将任务抛弃
DiscardOldestPolicy: 当线程池和队列都满时,再有任务进来,抛弃最老的未处理的任务即当前队列中排在最前面的任务,然后重试该新进来的任务,如果线程池关闭,那么任务将会被抛弃

阻塞队列BlockingQueue

add(Object): 把 Object 加到 BlockingQueue 里,即如果 BlockingQueue 可以容纳,则返回 true, 否则招聘异常

offer(Object): 表示如果可能的话,将 Object 加到 BlockingQueue 里, 即如果 BlockingQueue 可以容纳,则返回true,否则返回false.

put(anObject): 把 Object 加到 BlockingQueue 里, 如果 BlockQueue 没有空间,则调用此方法的线程被阻塞直到BlockingQueue里面有空间再继续.

poll(time): 取走 BlockingQueue 里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null

take(): 取走 BlockingQueue 里排在首位的对象,若 BlockingQueue 为空, 阻塞进入等待状态直到Blocking有新的对象被加入为止

核心线程数的设置

任务类型:
cpu密集型 不要超过cpu并行线程数:Runtime.getRuntime().availableProcessors()+1

io密集型 Runtime.getRuntime().availableProcessors() x2

线程进入顺序

corePoolSize —> BlockingQueue —> maximumPoolSize

若最大线程数也满了,再进入的线程按设置的拒绝策略执行。

java内存

文章目录
  1. 1. 为什么要使用线程池
  2. 2. 线程池的使用
  3. 3. 线程池中几个参数的含义
  4. 4. 四种拒绝策略
  5. 5. 阻塞队列BlockingQueue
  6. 6. 核心线程数的设置
  7. 7. 线程进入顺序
|