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组件上。

  • 相关阅读:
    HDU5120
    POJ 1062
    POJ 1086
    BestCoder 1st Anniversary (HDU 5311)
    HDU 5284
    Dylans loves sequence(hdu5273)
    day65 作业
    第三次小组分享 猴子补丁
    day59 csrf auth
    day58 cookie session 中间件
  • 原文地址:https://www.cnblogs.com/could-deng/p/6767622.html
Copyright © 2011-2022 走看看