背景:在我们用JAVA写PC端程序时,很可能会出现在客户端UI中直接进行耗时运算/联网操作的情况,虽然并不会报错,但这会导致UI更新缓慢,交互效果并不理想。而在android开发中,UI线程是绝对绝对不能有联网操作的,一旦有联网操作,程序就会崩溃。
以下列举一个很多初学者由于对多线程理解不深容易遇到的坑:
public class ClientThread extends Thread{ public ClientThread() { this.setName("客户端子线程"); } @Override public void run() { // TODO Auto-generated method stub super.run(); /** * 循环接收来自服务器的消息 * */ } public void sendMsg2Server() { System.out.println(currentThread()+"向服务器发送消息"); } } public class UI { public static void main(String[] args) { ClientThread ct= new ClientThread(); ct.start(); ct.sendMsg2Server(); } }UI线程调用客户端线程发送消息给服务器模拟。
ct.sendMsg2Server();这条语句看起来好像是调用了客户端子线程在执行向服务器发送消息的任务,那么实际情况是怎样的呢?让我们看一下输出结果:
原因分析:ClientThread在是一个线程的同时也是一个类,所以其中定义的方法实际上就是类中的成员方法,成员方法存储在内存中线程可以共享的方法区中,而并不是堆中存储的实例里,发起ct.sendMsg2Server();这条语句,调用这个成员方法的,实际上是UI线程,实际过程是:UI线程从方法区中找到ct.sendMsg2Server();这个方法放到自己私有的栈中执行,全过程子线程并不参与。也就是说,在程序装入虚拟机时,子线程就已经和它的成员方法分离了。
如果想调用子线程向服务器发送消息,那么就只能再开线程了。
解决方法:
public class RecvThread extends Thread{ public RecvThread() { this.setName("客户端接收子线程"); } @Override public void run() { // TODO Auto-generated method stub super.run(); /** * 循环接收来自服务器的消息 * */ } } import java.util.concurrent.ArrayBlockingQueue; public class SendThread extends Thread{ ArrayBlockingQueue<Object> msgQue = new ArrayBlockingQueue<Object>(1); public SendThread() { this.setName("客户端发送子线程"); } @Override public void run() { while(true) { Object o; try { o = msgQue.take();//在这里阻塞,等待用户在UI界面操作后 UI线程返回的消息 sendMsg2Server(o); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void sendMsg2Server(Object o) { System.out.println(currentThread()+"向服务器发送消息:"+o); } } public class UI { public static void main(String[] args) throws InterruptedException { //发送线程和接收线程分开 RecvThread rt= new RecvThread();//接收线程 rt.start(); SendThread st=new SendThread();//发送线程 st.start(); Thread.sleep(1000);//用户在这里进行操作 Object o = "用户操作返回的数据"; st.msgQue.put(o);//把数据传给专用的发送线程 } }这是正确的做法,发送和接收各自用了一个线程。这样不仅可以在正确地调用子线程发送消息还可以在发送的同时接收消息。让我们来看下结果:
没有问题!
当然,方法有很多这只是其中一种。欢迎大家指正、交流。
本篇介绍java多线程中UI线程和子线程通信/调用子线程向服务器发送数据的坑,主要是为下篇打个基础。
下篇将正式介绍如何应对android中UI线程不能联网(即UI线程调用子线程向服务器发送消息)和子线程接收到消息后如何通知UI线程进行更新。敬请期待。