美文网首页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

    1,Thread 线程是Java语言的一个概念,是实际执行任务的最小单元 启动线程 2,实现接口,并且实现run方...

  • java 多线程

    多线程: 说到多线程,最先提到的就是Thread和Runnable。实现多线程可以通过继承Thread 或者 实现...

  • 两种建立多线程的方式

    Thread和Runnable实现多线程的区别 Java中实现多线程有两种方法:继承Thread、实现Runnab...

  • 3-Java中如何创建线程

    Runnable和Thread实现多线程的区别 Java中实现多线程有两种方法:继承Thread类、实现Runna...

  • Thread,Runnable和Callable

    Java多线程实现方式:Thread,Runnable,Callable多线程是乱序执行Thread===1.Th...

  • 多线程学习-第一天

    多线程学习-第一天 1 多线程的实现方式 1.1 继承Thread类 通过继承Thread类并重写run方法实现:...

  • 2018-09-15

    多线程 方法 (继承Thread 实现Runna...

  • 4 多线程00

    一 多线程的创建 1、thread/runnablethread:继承Thread类runnable:实现Runn...

  • Java之多线程4种创建

    继承Thread类实现多线程。覆写Runnable()接口实现多线程。覆写Callable接口实现多线程。通过线程...

  • Java多线程1

    创建多线程的3种方式 1、继承Thread类实现多线程 2、实现Runnable接口方式实现多线程 定时器 线程的...

网友评论

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

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