zoukankan      html  css  js  c++  java
  • 多线程操作

     

    对于多线程的操作:有两种方式:

    1.继承Thread类。new Thread(){run()}.start();

    2.实现Runnable类。new Thread(){new Runnable(){}}.start();

    对于多线程的异步操作,处理方式有两种:

    1. Handler+Thread方式,即对Looper对象的管理
    2. AsyncTask
    3. 多线程的封装-线程池(线程池工厂或者自定义线程)

    Handler+Thread方式:

    对于Handler+Thread分为主线程与副线程的操作,只要我们对Looper对象操作即可,将消息message或者runnable进行send或者post即可。

    主线程:runOnUiThread(runnable action)、view.post(runnable action)、主线程的Handler.post(runnable action)、Handler.sendMessage(message)

    副线程:副线程的Handler.post(runnable action)

    对于主线程的handler.sendMessage()不再阐述,在activity上声明一个Handler,默认handler构造器就是与主线程的Looper绑定,即在Handler的handleMessage()都是在主线程上的操作。

    对于无论主线程还是副线程,针对handler.post(runnable action)的情况,Hanlder的封装如下:

    /**
         * UI线程 handler
         **/
    private static Handler mUiHandler;
    
    /**
        *副线程handler
        **/
    private static Handler SUB_THREAD1_HANDLER;
    
    /**
         * 副线程1
         */
    private static HandlerThread SUB_THREAD1;
    /**
        * 锁
        **/
    private static final Object mMainHandlerLock = new Object();
    
    /**
         * 取得UI线程Handler
         *
         * @return
         */
        public static Handler getMainHandler() {
            if (mUiHandler == null) {
                synchronized (mMainHandlerLock) {
    //                if (mUiHandler == null) {
                    mUiHandler = new Handler(Looper.getMainLooper());
    //                }
                }
            }
            return mUiHandler;
        }
    
    /**
         * 获得副线程1的Handler.<br>
         * 副线程可以执行比较快但不能在ui线程执行的操作.<br>
         * 此线程禁止进行网络操作.如果需要进行网络操作.
         * 请使用NETWORK_EXECUTOR</b>
         *
         * @return handler
         */
        public static Handler getSubThread1Handler() {
            if (SUB_THREAD1_HANDLER == null) {
                synchronized (ThreadManager.class) {
                    SUB_THREAD1 = new HandlerThread("SUB1");
                    SUB_THREAD1.setPriority(Thread.MIN_PRIORITY);//降低线程优先级
                    SUB_THREAD1.start();
                    SUB_THREAD1_HANDLER = new Handler(SUB_THREAD1.getLooper());
                }
            }
            return SUB_THREAD1_HANDLER;
        }

     view.post()与handler.post()的区别,使用不当可能导致内存泄露:

    相关:http://blog.csdn.net/a740169405/article/details/69668957

    如果调用view.post()方法的线程对象被GC-root引用(慎用static修饰Thread),则会造成内存泄漏

    因为:

      GC-root->Thread->ThreadLocal->ViewRootImpl->runnable

    但是主线程除外,因为虽然主线程的runQueue无法被回收,但是每次执行完performTraversals(),都会将runQueue中的对象执行并清除。

    经常我们将线程池对象定为static进行复用,因此需要慎用view.post().

      

     runOnUiThread(runnable action)与view.post(runnable action)的对比

    共同点:第一种runOnUiThread,和第二种view.post()都是要求主线程执行,都是通过获取主线程的handler,通过handler.post(),把action封装成message传到Looper的消息循环中,当handler再次处理该message时,直接调用action的run方法。

    区别:view.post(runnable action)是在view初始化完成后才执行post(runnnable),而runOnUiThread()是主线程立即执行,如果在onCreate()进行初始化activity时,需要对布局文件中的控件进行调整时,直接使用runOnUiThread代替view.post可能会造成view的信息获取都为空。

    因为在activity创建时,生命周期onCreate(),布局文件初始化还没完成,在onCreate()方法上,仅仅是将控件的相关信息写入内存,还没映射到设备上,因此控件信息还没初始化,普遍上初始化完成要在onResume()方法之后。

    handler原理

    我们可以通过调用handler的post方法,把Runnable对象(一般是Runnable的子类)传过去;handler会在looper中调用这个Runnable的Run方法执行。

    Runnable是一个接口,不是一个线程,一般线程会实现Runnable。所以如果我们使用匿名内部类是运行在UI主线程的,如果我们使用实现这个Runnable接口的线程类,则是运行在对应线程的。

    View.post原理

    View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

     拓展

    如果我们要进行耗时的操作,比如下载操作,我们不能再UI线程;也不能直接在副线程直接进行下载,因为我们要考虑多线程直接的顾虑,我们引入线程池。

    /**
         * AsyncTask的默认线程池Executor. 负责长时间的任务(网络访问) 默认3个线程
         */
        public static final Executor NETWORK_EXECUTOR;
        static {
            NETWORK_EXECUTOR = initNetworkExecutor();
        }
    private static Executor initNetworkExecutor() {
            Executor result;
            // 3.0以上
            if (Build.VERSION.SDK_INT >= 11) {
                //result = AsyncTask.THREAD_POOL_EXECUTOR;
                result = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<Runnable>());
            }
            // 3.0以下, 反射获取
            else {
                Executor tmp;
                try {
                    Field field = AsyncTask.class.getDeclaredField("sExecutor");
                    field.setAccessible(true);
                    tmp = (Executor) field.get(null);
                } catch (Exception e) {
                    tmp = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                            new LinkedBlockingQueue<Runnable>());
                }
                result = tmp;
            }
    
            if (result instanceof ThreadPoolExecutor) {
                //线程数改为CPU 核数 +1
                ((ThreadPoolExecutor) result).setCorePoolSize(FitmixUtil.getPhoneCpuCoreNum() + 1);
            }
    
            return result;
        }
    /**
         * 在网络线程上执行异步操作. 该线程池负责网络请求等操作 长时间的执行(如网络请求使用此方法执行) 当然也可以执行其他 线程和AsyncTask公用
         *
         * @param run
         */
        public static void executeOnNetWorkThread(Runnable run) {
            try {
                NETWORK_EXECUTOR.execute(run);
            } catch (RejectedExecutionException e) {
            }
        }

     AsyncTask

    引言:

    我不太同意封装好就会影响性能的说法,在我实际的运用中,真正的缺点来自于AsyncTask的全局线程池只有5个工作线程,也就是说,一个APP如果运用AsyncTask技术来执行线程,那么同一时间最多只能有5个线程同时运行,其他线程将被阻塞(注:不运用AsyncTask执行的线程,也就是自己new出来的线程不受此限制),所以AsyncTask不要用于多线程取网络数据,因为很可能这样会产生阻塞,从而降低效率。

    AsyncTask是封装好的线程池,比起Thread+Handler的方式,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了;

    一个异步任务的执行一般包括以下几个步骤:

    1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

    2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

    3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

    4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

    5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

  • 相关阅读:
    Python 学习日记 第七天
    Python 学习日记 第六天
    Python 学习日记 第五天
    Python 学习日记 第四天
    Redis 中的数据类型及基本操作
    Asp.net mvc 中View 的呈现(二)
    Asp.net mvc 中View的呈现(一)
    Asp.net mvc 中Action 方法的执行(三)
    Asp.net mvc 中Action 方法的执行(二)
    Asp.net mvc 中Action 方法的执行(一)
  • 原文地址:https://www.cnblogs.com/could-deng/p/6767622.html
Copyright © 2011-2022 走看看