背景
年初开发了一个项目,项目中有这样一个需求,需要每隔一秒钟向服务器发起一次状态信息查询指令,在这个过程中偶尔会主动发起一次状态信息修改的指令(PS:因为服务器的处理能力有限,必须间隔一秒才能发送命令,否则指令将不会被执行)。本来不是什么大问题,但是由于开发人员从事android开发时间较短,多线程用得也不多,所以看似简单的问题,最后使得整个项目出现了较大的性能问题。下面主要讲讲如何一步步的优化过程。(本文适合初学者,大牛可以绕道前行)。
优化前
以下是功能部分代码
public void sendOrderToServer(final byte[] data) {
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
order_queue.offer(data);//将指令放入队列中
if (null != order_queue && order_queue.size() != 0) {
synchronized (lock) {
while (order_queue.size() != 0) {
try {
byte[] orderData = order_queue.poll();//取出队首指令
int dataLength = orderData.length;
if (dataLength > 0) {
//发送指令到服务器
}
try {
Thread.sleep(1000);//等待一秒钟,然后执行下一条指令
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
});
thread.start();
}
代码很简单,相信大家都能看懂是什么意思,阅读下来好像也没什么太大问题。模拟器运行一遍,没有问题,各项数据显示正常。然后信心满满安装到真机上,拍拍手,欧✌️,搞定。可惜理想是美好的,现实是残酷的,分分钟啪啪啪打脸。app运行不到一分钟,整个界面就开始卡顿了,操作屏幕毫无反应。这是为啥呢 ???
然后认真阅读了一下代码,发现里面用到了synchronized (lock)同步锁,笔者为什么要这样写,估计是考虑到指令需要间隔一秒钟才能发送,又要保证指令是按序的。由于app每次发送命令都需要调用sendOrderToServer方法,并创建新的线程。这就出现了一个问题,除了第一个线程能获取到同步锁,其他的线程都无法获取到,因为order_queue队列永远不会为空。那么其他线程都相当于死锁,都无法执行后面的代码,也就不能释放占用的系统资源。这样线程不停的创建,系统资源被一点点消耗掉,最后造成界面卡顿。既然问题找到了,那就好办了,其实优化方法有很多,下面只是其中一种。
优化后
public void sendOrderToServer(final byte[] data) {
order_queue.offer(data);
}
private class WriteThread extends Thread {
@Override
public void run() {
super.run();
while (true) {
if (null != order_queue && order_queue.size() != 0) {
byte[] orderData = order_queue.poll();
if (null != orderData) {
int dataLength = orderData.length;
if (dataLength > 0) {
//发送指令到服务器
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
只创建一个WriteThread对象,里面运行一个while (true)的循环,发送指令时只需要调用sendOrderToServer方法,往order_queue队列中加入指令就好。
总结
1、项目中使用多线程加锁时,需要注意不要造成死锁。
2、如果需要创建大量的线程,请使用线程池来管理,减少线程创建的开销,以及资源的浪费。