前言
前一阵去华为面试。技术面中,我提到平时常用的线程池为依据生成者-消费者关系写的线程池,较少使用Java-Executors的线程池,面试官疑问JAVA线程池不好用?我竟大言不惭的说JAVA线程池耗费资源,面试管的那个鄙视眼神,至今未忘怀^_^。
- 生产者与消费者线程池详解
- 耗费的资源是-线程切换
生成者与消费者线程池
概念在此不做介绍,不懂就不懂吧!
字面意思很容易理解撒,生成者生成产品,消费者消费产品,生成者,消费者均可以多个。你会说这和线程池有个diao关系。别急嘛,给大爷您笑个,整理整理心情再向下看。
例子来了:隔壁老王种了一棵苹果树成熟了,一树的苹果么有办法卖,就存在自家仓库,供自家人吃,当然还有隔壁胖红。
理解没?没理解再瞅这:苹果树-生成者。胖红等人-消费者。仓库呢,就是TMD池。胖红等不就是执行的常驻线程。苹果树就是资源的产生者,产生的资源(苹果)放入池(仓库)中,常驻线程消费者(胖红等人)消费资源。如果还没看懂,为您的智商表示担忧,还是建议你学好spring就够。
nounou上代码
代码仅是随手写的样例,不过向天发誓展示代码完整
消费者,由资源池tasks中取出资源task ,并对资源进行消费task.execute()。
import java.util.concurrent.BlockingQueue;
//我是消费者
public class ExecutTread extends Thread{
public ExecutTread(BlockingQueue<AbstractTask> tasks) {
this.tasks = tasks;//我是所有消费线程共享的资源池
}
BlockingQueue<AbstractTask> tasks = null;
volatile boolean run = true;
volatile boolean busy = false;
//AtomicBoolean busy = new AtomicBoolean(false);
@Override
public void run() {
// TODO Auto-generated method stub
//此处证明我的饭量不错
while(run) {
//取资源
AbstractTask task = tasks.poll();
if(task == null) {
ThreadCompare.waitIt(5);
continue;
}
busy = true;
try {
//消费资源
task.execute();
} finally {
busy = false;
}
}
}
}
该看看资源是个什么鸟
//我就是那个鸟,资源的爹
public abstract class AbstractTask {
abstract void execute();
}
看了半天,该骂爹了,说好的池呢,资源池炒好了,退不了了
package com.ccycc.rxjava.rxjavaL;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import com.google.common.collect.Lists;
public class ThreadManager {
//我tasks 就是传说中的资源池 大跌眼镜啊
BlockingQueue<AbstractTask> tasks = new LinkedBlockingQueue<AbstractTask>();
List<ExecutTread> treads = Lists.newArrayList();
//初始化消费者线程
void init() {
for(int i=0; i<10; i++) {
ExecutTread tread = new ExecutTread(tasks);
tread.start();
treads.add(tread);
}
}
//我只是向池中放资源的动作
void addTask(AbstractTask task) {
tasks.offer(task);
}
boolean isfinsih() {
if(tasks.size() > 0) {
return false;
}
for(ExecutTread tread :treads) {
if(tread.busy) {
return false;
}
}
return true;
}
}
好了生产者与消费的线程池完了,哎卧槽,生成者呢?看着也不像个线程池啊?线程池连个线程都么有。您试着将消费者线程强加到池里。有个池子,他里面由几个工作线程,把资源执行完和线程池里放很多线程,线程里包装资源不差不多道理。呵呵!
再看看:
package com.ccycc.rxjava.rxjavaL;
public class WorkTask extends AbstractTask{
//资源要执行的业务
@Override
void execute() {
// TODO Auto-generated method stub
try {
Thread.sleep(10);
//System.out.println("*****");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//
void cpthreadpool() {
ThreadManager manager = new ThreadManager();
long start = System.currentTimeMillis();
//在这初始化消费者
manager.init();
int i=0;
//在这生成资源
for(;i<1000000;i++) {
manager.addTask(new WorkTask());
}
while(true) {
if(!manager.isfinsih()) {
waitIt(500);
}else {
break;
}
}
System.out.println("总耗时:" + ((System.currentTimeMillis() - start)/1000));
}
最后,有人会说这垃圾的池,啥功能都么有,人家java-Exectors中的有各种策略,比如拒绝,超时了什么的。^_^不好意思,忘记了如果你能看到这,估计八成也不清楚那四种策略,罪过。
线程耗费资源
这是一个打脸的话题,java-Exectors的线程池,没有这破玩意牛逼?那当然不是,当然不是,当然不是。重要的话说三遍。因为我耿耿于怀华为面试人那个鄙视的眼神。我是来证明线程切换真真的耗费资源。不过么有那么严重的说,实时证明是几乎无感知,呵呵,前后矛盾,自己打自己脸,看测试结果吧。
package com.ccycc.rxjava.rxjavaL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadCompare {
public static void main(String[] args) {
ThreadCompare compare = new ThreadCompare();
//compare.javathreadpool();
compare.cpthreadpool();
}
//这个是java线程池
void javathreadpool() {
ExecutorService service = Executors.newFixedThreadPool(10);
int i=0;
long start = System.currentTimeMillis();
for(;i<1000000;i++) {
service.submit(new Worker());
}
service.shutdown();
while(true) {
if(!service.isTerminated()) {
waitIt(500);
}else {
break;
}
}
System.out.println("总耗时:" + ((System.currentTimeMillis() - start)/1000));
}
//这个是生成者与消费者的线程池
void cpthreadpool() {
ThreadManager manager = new ThreadManager();
long start = System.currentTimeMillis();
manager.init();
int i=0;
for(;i<1000000;i++) {
manager.addTask(new WorkTask());
}
while(true) {
if(!manager.isfinsih()) {
waitIt(500);
}else {
break;
}
}
System.out.println("总耗时:" + ((System.currentTimeMillis() - start)/1000));
}
static void waitIt(long timeout) {
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.ccycc.rxjava.rxjavaL;
public class Worker extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(10);
//System.out.println("*****");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
java线程池执行耗时:1037秒。
生成者与消费者线程池耗时:1031秒。
结果当然是支持我的,哎!重要的事情粗体线程切换耗费资源,上面的例子也只是为了证实这个概念。
到此可能还有人不懂?一脸懵逼。生成者与消费者线程池是不是仅有10个线程。java-Exectors种全部不都是线程,一个线程干完活,是不是要切换另一个线程。对,就是这。
转载请注明来源
欢迎大家沟通交流