先说结论:
1,如果需要很多线程执行任务,每个任务都不大的话,用线程池性能提升很高,10几倍左右。
2,线程池开的线程不用太多,和机器cpu内核量差不多就行。
3,如果有函数是被异步调用执行的话,要注意给它传入的参数,可能在函数执行时并不是你当初预想的参数值。
-------------------------------------------------------------------------------------------------------------------------------
贴对比的代码:
public class TestMain {
static final int TNum=10000;
static int i=0;
//用线程池
public void UseThreadPool(int count) {
long startTime = System.currentTimeMillis();
ThreadPoolExecutor tp = new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue(count));
for (i= 0; i < count; i++) {
tp.execute(new Runnable() {
@Override
public void run() {
fun0( );
}
});
}
tp.shutdown();
try {
tp.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("使用了线程池:");
System.out.println(System.currentTimeMillis() - startTime + "毫秒");
}
//不用线程池
public void unUseThreadPool(int count) {
long startTime = System.currentTimeMillis();
for ( i = 0; i < count; i++) {
Thread thread = new Thread() {
public void run() {
fun0();
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("未用线程池:");
System.out.println(System.currentTimeMillis() - startTime + "毫秒");
}
public static void main(String[] args) {
TestMain testpool = new TestMain();
testpool.UseThreadPool(TNum);
testpool.unUseThreadPool(TNum);
}
private Double fun0()
{
Random random = new Random();
String buf="abc"+random.nextDouble();
Double result=random.nextDouble()*random.nextDouble()*random.nextDouble();
int ak=0;
for (int i=1;i<100;i++)
{
ak=ak+i;
}
return result;
}
}
上面的代码线程池是4个线程,最多8个,执行后结果如下:
效果蛮好的,用了线程池性能提高10几倍。
我把线程池加大线程数量,到40个线程,最高80个,结果如下
这个和4线程差异不大,在变动范围内。
如果继续加大线程池开到4000个线程。则性能暴跌了
说明线程池的线程不用太对,估计只要和电脑cpu的核数量匹配就行。
----------------------------------------------------------------------------------------
然后,我想会不会是用了线程池后,线程没执行完就统计时间导致看起来性能高。
然后,我把上面的fun0()函数改了一下fun1:
private Double fun1(int par)
{
Random random = new Random();
String buf="abc"+random.nextDouble();
Double result=random.nextDouble()*random.nextDouble()*random.nextDouble();
int ak=0;
for (int i=1;i<100;i++)
{
ak=ak+i;
}
//if(par==(TNum-1))
{System.out.println("TNum:"+par);}
return result;
}
//用线程池的
for (i= 0; i < count; i++) {
{System.out.println("TNuma:"+(i+1));}
tp.execute(new Runnable() {
@Override
public void run() {
{System.out.println("TNumb:"+(i+1));}
fun1(i);
}
});
}
//不用线程池
for ( i = 0; i < count; i++) {
Thread thread = new Thread() {
public void run() {
//fun0();
fun1(i);
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
使用线程池的输出这样:
我原本希望fun1()收到的参数是0 1 2 3,但是看TNum的输出4个线程分别受到的参数是4 4 2 4。而从打印的TNuma 和TNumb的数据能看出,线程池里的线程是被异步执行的。正是因为线程池的execute()函数是异步的,它收到runnable后放入队列,所以for循环迅速执行完。
而线程池开始执行队列里的runnable的时候,才开始去调用fun1,并压入参数par,而这个时候i已经变了,并不是当初设想的0 1 2 3 了。
而未用线程池的输出是对的
-------------------------------------------------------------------------------------
所以对于被异步调用执行的函数,对它的参数需要单独保存
我这样改了一下
class MyRunnable implements Runnable{
public int par;
public MyRunnable(int par)
{
this.par=par;
}
@Override
public void run() {
// TODO Auto-generated method stub
fun1(par);
}
}
然后
//线程池
for (i= 0; i < count; i++) {
tp.execute(new MyRunnable(i));
}
//未用线程池
for ( i = 0; i < count; i++) {
Thread thread = new Thread(new MyRunnable(i)) ;
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这样参数就正常了