美文网首页Android高级进阶
2019-07-30-Android 多线程的实现:Thread

2019-07-30-Android 多线程的实现:Thread

作者: 王元 | 来源:发表于2019-08-04 10:27 被阅读0次

    1,Thread

    线程是Java语言的一个概念,是实际执行任务的最小单元

    public class MyThead extends Thread {
        @Override
        public void run() {
            super.run();
            //执行具体的业务逻辑
        }
    }
    

    启动线程

    public void startThead() {
        MyThead thead = new MyThead();
        thead.start();
    }
    

    2,实现接口,并且实现run方法

    public class MyRunable implements Runnable {
        @Override
        public void run() {
            //具体的业务逻辑
        }
    }
    

    启动runable

    Thread thread = new Thread(new MyRunable());
    thread.start();
    

    Android应用中各种类型的线程,本质都是Linux系统的pthreads,在应用层分为各种线程

    • 主线程:也称为UI线程,随着应用的启动而启动。主线程用来运行Android组件,同时刷新UI屏幕上的UI元素,只有主线程才能更新UI,因为Android的UI工具包不是线程安全的
    • Binder线程:用于不同进程之间的通信,每个进程都维护着一个线程池,用来处理其他进程发来的消息,这些进程包括系统服务,Intents,ContentProdiver,Service等。一个典型的需要是使用Binder线程的场景是应用程序提供给一个给其他进程通过AIDL接口绑定的service。
    • 后台线程:应用程序创建的线程都是后台线程,在Android框架里,WindowManager赋予了主线程只能更新UI的限制,而其他线程不能更新UI

    2,HanderThead

    HandlerThread是一个集成了Looper和MessageQueue的线程,当启动HandlerThread的时候,会同时创建一个looper和MessageQueue,然后等待消息去处理,下面是run方法的源码

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

    它的使用方法和普通的线程是一样的,方法如下:

    public void usehandlerThread() {
        HandlerThread thread = new HandlerThread("use-handlethread");
        thread.start();
        Handler mHandler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    }
    

    HandlerThread只有一个消息队列,队列的消息是顺序执行的,因此是线程安全的,当然吞吐量会收到一定的影响.

    HandlerThread的内部机制确保了,在创建lopper和发送消息之间不存在竞态条件,这是通过HandlerThread.getLooper()实现为一个阻塞操作实现的,只有当HandlerThread准备好接受消息的时候才会返回

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // 如果线程已经启动,那么在looper准备好之前,应该先等待
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    

    如果某些业务下,需要在HandlerThread开始接收消息前执行一些操作,比如初始化Handler,可以继承HandlerThread来做

    public class MyHandlerThread extends HandlerThread {
        public MyHandlerThread(String name) {
        super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
        }
        
        private Handler mHandler;
        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            mHandler = new Handler(getLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
        }
            
        public  void uploadMsg(Message message) {
            if(mHandler != null)mHandler.sendMessage(message);
        }
    }
    

    3,AsyncQueryHander

    4,IntentService

    为了能够在Service内实现在子线程中执行耗时任务,Android
    引入了IntentService,但依旧使用的是HanderThread

    本身就是一个Service,可以通过context.startService启动

    IntentServie是一个抽象类,需要我们使用前继承,并实现onHandleIntent(Intent intent);方法,同时在子类的构造方法中调用super(String name)传入子类名字

    public class MyIntentService extends IntentService {
        /**
         * Creates an IntentService.  Invoked by your subclass's constructor.
         *
         * @param name Used to name the worker thread, important only for debugging.
         */
        public MyIntentService(String name) {
            super(MyIntentService.class.getName());
            setIntentRedelivery(true);
        }
    
        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            // 这个方法在后台线程中调用,原因是IntentService内部的handler绑定的是自线程的looper
        }
    }
    

    上面代码中setIntentRedelivery方法如果设置为true,那么IntentService的onstartCommand方法将返回START_REDELIVER_INTENT,这时如果onHandleIntent在返回前被进程杀死掉了。那么进程将重新启动,Intent将重新投递。

    5,Java线程池 Executer Framwork

    我们知道创建和销毁对象,是存在开销的(例如线程),这些会影响我们的性能,使用Java Executer框架可以通过线程池解决这一问题,Executer框架提供了如下能力

    • 创建工作线程,同时通过队列来控制能够在这些线程执行任务的个数
    • 检测导致线程意外终止的错误
    • 等待线程执行完毕并获取执行结果
    • 批量执行线程,并通过固定的顺序获取执行结果
    • 在合适的时机启动后台线程,从而保证线程执行的结果很快反馈给用户

    Executer是一个接口,主要目的就是分离任务的创建和执行,实现上面的功能点

    public interface Executor {
        void execute(Runnable command);
    }
    

    当需要实现自己的线程池的时候。只有实现Executor接口,并实现execute方法,最简单的实现就是创建线程执行任务

    public class MyExecutor implements Executor {
        @Override
        public void execute(Runnable command) {
            new Thread(command).start();
        }
    }
    

    当然实际应用的Executor很少是这么简单的,需要增加类似队列,任务优先级等功能,最终实现线程池

    线程池是任务队列和工作线程的集合,这俩者结合起来实现生产者和消费者模式

    Executor已经为我们提供了几个预定义的线程池实现,如下

    • 固定大小的线程池,通过Executors.newFixedThreadPool(n),n表示线程池中线程的大小

         public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
      
    • 可变大小的线程池,通过Executors.newCachedThreadPool()创建

    当有新的任务需要处理的时候,线程会创建新的线程来处理它

    空闲的线程会等待60s来执行新任务,当没有可执行的任务,就会自动销毁

    因此线程池的大小会根据队列的大小变化而变化

    • 单个线程的线程池,通过Executors.newSingleThreadExecutor()创建,这个线程池中永远只有一个线程串行的执行任务队列中的任务

    以上就是预定义的线程池,它们都是基于ThreadPoolExecutor类的基础上构建的,而通过ThreadPoolExecutor我们可以自定义线程池的一些行为。

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue)
    
    • corePoolSize:核心线程数。核心线程会一直存在线程池中,即使当前没有任务需要处理,当线程数小于核心线程数,即使当前有空闲的线程,线程池也会优先创建新的线程来执行新任务,这样做的好处是无需缓存
    • maximumPoolSize:最大线程数。当线程数大于核心线程数,而且任务队列已经满了,这是线程池会创新新的线程来执行任务,直到线程数等于最大线程数为止
    • keepAliveTime:线程空闲存活时间。当线程的空闲时间超过keepAliveTime时间时,线程会被销毁,知道线程数等于核心线程数
    • unit:keepAliveTime的时间单位。可选的值有TimeUnit的枚举值
    • workQueue 线程池锁使用的任务缓存队列

    6,AsyncTask

    AsyncTask时在Executors框架基础上的封装,它实现了耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信

    public class MyAsyncTask extends AsyncTask<Integer, Integer, String> {
    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //主线程
        }
    
        @Override
        protected String doInBackground(Integer... integers) {
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Integer[] values) {
            super.onProgressUpdate(values);
            //主线程
        }
    
        @Override
        protected void onPostExecute(String o) {
            super.onPostExecute(o);
            //主线程
        }
    }
    

    我们知道在不同的Android版本中对AsycTask的实现不尽相同,在不同的版本的表现区别如下

    API level execute方法 executeOnExecutor方法
    1~3 串行执行 没有这个方法
    4~10 并行执行 没有这个方法
    11~12 并行执行 串行或者并行
    13+ 串行执行 串行或者并行

    可以看到在api 13+建议使用executeOnExecutor方法代替execute

    一个应用中所使用的所有Async实例会共享全局的属性,也就是说如果AsyncTask中的任务时串行执行,那么应用中的所有AsyncTask都会排队,只有等前一个任务执行完成,才会执行下一个任务
    如果AsyncTask执行的是异步任务,那么在4核CPU系统上最多也只有5个任务同时进行,其余任务需要在队列中排队,等待空闲的线程,原因是AsyncTask中指定的核心线程数是CPU核数+1

    7,Loader

    Loader 是Android3.0开始引入的一个异步数据加载框架,这里暂时不做更多介绍,以后补充

    相关文章

      网友评论

        本文标题:2019-07-30-Android 多线程的实现:Thread

        本文链接:https://www.haomeiwen.com/subject/vzuwrctx.html