多线程和异步处理

本文介绍了多线程的两种实现方式,包括继承Thread类和实现Runnable接口,并通过示例对比了它们的差异。推荐使用实现Runnable接口的方式,因为这种方式更高效且允许实现多个接口。此外,还探讨了Handler在更新UI和处理子线程耗时操作中的作用,以及Handler与MessageQueue的区别。最后,讲解了AsyncTask的使用及其执行准则,提供了一个网络加载图片的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多线程

有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口


继承Thread Demo

    class MyThread extends Thread {
        private String name;
        private int ticket;

        public MyThread(String name, int ticket) {
            super();
            this.name = name;
            this.ticket = ticket;
        }

        @Override
        public void run() {
            while (this.ticket > 0) {
                //加上休眠是为了更好的看出效果
                try {
                    Log.e("线程开始: " + name, " 卖票:ticket " + this.ticket--);

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

启动线程:

        MyThread mt1 = new MyThread("线程a", 10);
        MyThread mt2 = new MyThread("线程b", 15);
        MyThread mt3 = new MyThread("线程c", 20);
        mt1.start();
        mt2.start();
        mt3.start();

通过打印的log,我们可以看出,线程启动的先后顺序并不相互影响


实现Runnable接口 Demo

    class MyRunnable implements Runnable {
        private String name;
        private int ticket;

        public MyRunnable(String name, int ticket) {
            super();
            this.name = name;
            this.ticket = ticket;
        }

        @Override
        public void run() {

            while (this.ticket > 0) {
                //加上休眠是为了更好的看出效果
                try {
                    Log.e("线程开始: " + name, " 卖票:ticket " + this.ticket--);

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

启动线程:

        MyRunnable myRunnable = new MyRunnable("线程d", 45);
        new Thread(myRunnable).start();
        new Thread(myRunnable).start();
        new Thread(myRunnable).start();

通过比较第一种和第二种线程的打印的log,我们可以发现第一种启动方式,是每个线程分配多少任务就完成多少任务,并不会共享资源;而第二种方式,共享一个资源myRunnable 的票数ticket

通俗来讲,第一种方式是大家各自做各自的事情,各不影响;第二种方式是大家一起做某件事情,也是互不影响各自的进程,数学比较好的各位应该也就可以看出,第二种方式的效率要较高一些。

除此之外,我们都知道在代码里我们只可以继承一个类,但是可以实现多个接口,由于以上两点,我们推荐使用Runnable 方式实现多线程的目的

拓展 –Handler

更新UI需要在主线程里进行,网络请求等耗时操作需要在子线程里实现,那么我如何在耗时操作完成后去更新UI呢,这就需要Handler了,直接看Demo

        new Thread(new Runnable() {
            @Override
            public void run() {
                //TODO 耗时操作

                Bundle bundle = new Bundle();
                bundle.putInt("key", 10);

                Message message = mHandler.obtainMessage();
                message.what = 1;
                message.setData(bundle);
                message.sendToTarget();

            }
        }).start();

除了这种方式,还有其他的方式

//message由handler创建,可直接向handler发送消息
      Message msg = mHandler.obtainMessage();
      msg.sendToTarget();

//message通过new的方式创建,可用handler.sendMessage(msg)来发送消息
      Message msg = new Message();
      mHandler.sendMessage(msg);

//message通过Message.obtain的方式创建,可用sendMessage(msg)来发送消息 
      Message msg = Message.obtain();
      mHandler.sendMessage(msg);

那么Message message = mHandler.obtainMessage()Message message = new Message()有什么区别呢:

obtainmessage()是从消息池中拿来一个msg ,不需要另开辟空间new
new需要重新申请,效率低,obtianmessage可以循环利用。

已经发送信息后,如何处理:

    Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 1) {
                int result = msg.getData().getInt("key");
                //TODO UI处理
                Toast.makeText(MainActivity.this, "结果为" + result, Toast.LENGTH_SHORT).show();
            }
            return false;
        }
    });

在这边也有个小小的拓展:

    Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

public boolean handleMessagepublic void handleMessage有什么区别呢:

我查了一下,主要的区别的boolean handleMessage的返回值决定继续传递给void handleMessage处理信息

如果不使用Callback()boolean handleMessage,我们是默认消息会传递给void handleMessage处理信息

但是使用起来有什么区别,我现在还没有看出来,如果有知道的,也可以告诉我一下,互相学习学习。

除了使用Handler来更新UI,还有一下几种方式更新UI


        // 使用 mHandler.post
        new Thread(new Runnable() {
            @Override
            public void run() {
                //TODO 耗时操作

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //TODO 更新UI
                    }
                });

            }
        }).start();

        // runOnUiThread
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //TODO 更新UI
            }
        });

        //  控件的post
        ImageView.post(new Runnable() {
            @Override
            public void run() {
                //TODO 更新UI ImageView
            }
        });

异步处理 AsnyncTask

直接上网络加载图片的Demo

public class DownImage extends AsyncTask<String, Void, Bitmap> {

    private ImageView imageView;

    public DownImage(ImageView imageView) {
        this.imageView = imageView;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        //TODO 在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。负责执行那些很耗时的操作。必重写

        String url = params[0];
        Bitmap bitmap = null;
        try {
            InputStream is = new URL(url).openStream();
            bitmap = BitmapFactory.decodeStream(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        //TODO 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread。必重写
        imageView.setImageBitmap(result);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //TODO 执行实际的后台操作前被UI thread调用,可以做一些准备工作,如显示进度条,按需求决定是否重写
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        //TODO UI thread展示任务的进展情况,可以通过一个进度条进行展示,按需求决定是否重写
    }
}

调用:

new DownImage(imageview).execute(url);

解释:

AsyncTask的三个泛型参数说明(三个参数可以是任何类型)

demo :

public class DownImage extends AsyncTask<String, Void, Bitmap>{
                ...
}

第一个参数:传入doInBackground()方法的参数类型
第二个参数:传入onProgressUpdate()方法的参数类型
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。


AsyncTask遵守准则 :

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

  • Task的实例必须在UI thread中创建
  • execute方法必须在UI thread中调用
  • 不要手动的调用onPreExecute(),
    onPostExecute(Result),doInBackground(Params…),
    onProgressUpdate(Progress…)这几个方法

参考文章:AsyncTask异步处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值