最近做一个小程序,要求不难,就是一个有若干Client连接到一个Server上,然后Client可以向Server发消息,Server把这个消息存入MySQL,并转发给各个Client 。我的大致思路是:每个Client上启动两个线程,一个ClientRecevieThread负责接收Server传来的消息,另一个ClientSendThread负责将自己的消息发送给Server。然后Server上针对每个连接的Client启动一个ClientThread线程,负责接收这个Client传来的消息,Server上还要启动一个总的线程SendThread,由其负责向所有Client转发消息。在具体实现时:每当有Client连接上Server时,即将其add到一个clientList里面,如果这个Client退出,则再remove掉。在发送消息的过程中,ClientSendThead先将msg发送给Server上的ClientThread,由这个ClientThread纪录下收到的消息的时间,消息的发送者等信息,并将他们一起封装到一个自定义的Message类中,并将这个Message加到一个用LinkedList实现的消息队列msgList的末尾。Server上的SendThread则依次读取msgList中的Message,把他们保存到MySQL中,并将其中的msgContent发送个clientList里面的客户端,做完这些事情后,即msgList.removeFirst()。整个过程如下所示:
这个程序我一开始是用线程池实现的。采用了ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)这种构造方法构造出一个pool,使用pool.execute方法启动各个线程。在我模拟多用户同时登陆并发送消息时,发现采用线程池时,程序的运算速度很慢。后来我又采用直接让线程自己start()的方法,发现速度提高很快。以下是我统计出的一个表格:
采用线程直接启动方式 | |||||
客户端数量 | 发送的消息数 | 最大活动线程 | 最小活动线程 | 总时间(秒) | 转发消息所花最大时间(秒) |
2000 | 10+1 | 250 | 3 | 18 | 0.062 |
2000 | 3+1 | 375 | 3 | 13 | 0.047 |
1000 | 10+1 | 240 | 3 | 7 | 0.031 |
1000 | 3+1 | 316 | 3 | 9 | 0.047 |
500 | 10+1 | 254 | 3 | 5 | 0.031 |
500 | 3+1 | 333 | 3 | 3 | 0.032 |
采用线程池方式启动线程 | |||||
客户端数量 | 发送的消息数 | 最大活动线程 | 最小活动线程 | 总时间(秒) | 转发消息所花最大时间(秒) |
1000 | 10+1 | 964 | 5 | 291 | 27.959 |
1000 | 3+1 | 379 | 5 | 55 | 15.991 |
500 | 10+1 | 553 | 5 | 64 | 6.270 |
500 | 3+1 | 457 | 5 | 9 | 6.670 |
结果已经很明显了,所以2000用户发消息的线程池模拟我就没做。照理来说,采用线程池应该能提高程序性能才对。但是在这个程序中采用线程池却导致了性能的大幅下降。至于其中原因我现在也不是特别清楚。