总结:Android中的线程,线程池相关(一)---线程

前言

把一个相对耗时且数据操作复杂的任务分割成多个小的操作,然后分别运行在多个线程上,这能够提高完成任务的速度和效率。在多核CPU的设备上,系统可以并行运行多个线程,而不需要让每个子操作等待CPU的时间片切换。例如,如果要解码大量的图片文件并以缩略图的形式把图片显示在屏幕上,当你把每个解码操作单独用一个线程去执行时,会发现速度快了很多。
本篇博客总结本人在Android学习中所遇到的线程相关问题.


一.线程介绍

ThreadRunnable只是两个基本的线程类,通过他们能发挥的作用有限,但是他们是强大的Android线程类的基础类,例如Android中的HandlerThread, AsyncTaskIntentService都是以它们为基础。ThreadRunnable同时也是ThreadPoolExecutor类的基础。ThreadPoolExecutor类能自动管理线程和任务队列,甚至可以并行执行多个线程。

1.1实现线程的方法

 1. 继承`Thread`类
 2. 实现`Runnable`接口
 3. 实现`callable`接口 

下面简单记录如何使用这三种方法实现线程,重点记录第三种方法.

1.1.1 继承Thread

private class DownloadThread extends Thread {
...
       @Override
       public void run() {

       // 把当前的线程变成后台执行的线程
       android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);              
       /*
        * 把你想要在线程中执行的代码写在这里
        */
        ...
    }

    }

1.1.2 实现Runnable接口

private class DownloadThread implements Runnable {
        ...
        @Override
        public void run() {
         // 把当前的线程变成后台执行的线程
              android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);      
        /*
         * 把你想要在线程中执行的代码写在这里
         */
         ...
        }
    }

在一个类里,Runnable.run() 包含执行了的代码。通常在Runnable 中执行任何操作都是可以的,但需要记住的是,因为Runnable 不会在UI线程中运行,所以它不能直接更新UI对象,例如View 对象。

注意:
在Runnable.run())方法的开始的地方通过调用参数为THREAD_PRIORITY_BACKGROUND 的Process.setThreadPriority()方法来设置线程使用的是后台运行优先级。

1.1.3 实现callable接口

首先看看源码中对callablerunnable的定义

public interface Callable<V> {  
    /** 
     * Computes a result, or throws an exception if unable to do so. 
     * 
     * @return computed result 
     * @throws Exception if unable to compute a result 
     */  
    V call() throws Exception;  
}  
public interface Runnable {

    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}

下面说说两者之间的区别:

Runnable和Callable的区别:

  • Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
  • Callable规定的方法是call(),Runnable规定的方法是run()
  • Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)
  • call方法可以抛出异常,run方法不可以
  • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法isDone()以等待计算的完成,并检索计算的结果get(),取消计算cancle(boolean).通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
  • 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

示例代码:

public class OneTask implements Callable<String> {//callable有个<V>,这个V就是call函数的返回值类型  
    private int id;  
    public OneTask(int id){  
        this.id = id;  
    }  
    @Override  
    public String call() throws Exception {//这儿可以抛出异常,而Runnable接口的run函数不可以  
        int i=5;  
        while (i>=0) {  
            System.out.println("Thread "+ id +" is working");  
            Thread.sleep(1000);  
            i--;  
        }  
        return "result of Test2 " + id; //Runnable接口的run函数是没有返回值的  
    }   
}  

测试类:

public class Test1 {   

    public static void main(String[] args) {  
        Callable<String> oneCallable = new OneTask(1);  
        FutureTask<String> ft= new FutureTask<String>(oneCallable);  
        //FutureTask<String>是一个包装器,它通过接受Callable<String>来创建,它同时实现了Future和Runnable接口  

        new Thread(ft).start();  

        while(!ft.isDone()){  
            try {  
                System.out.println("检查线程执行完了吗...");  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {   
                e.printStackTrace();  
            }  
        }  

        String result = "";  
        try {  
            result = ft.get();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        System.out.println(result);  
    }  
}  

其中,FutureTask扮演监督的角色,主线程通过不断询问实现Callable的类对应的线程是否执行完毕,最后可以得到返回的结果.

其中Future源码如下:

    public interface Future<V> {  

        boolean cancel(boolean mayInterruptIfRunning);      

        /** 
         * Waits if necessary for the computation to complete, and then 
         * retrieves its result. 
         * 
         * @return the computed result   
         */  
        V get() throws InterruptedException, ExecutionException;  


        V get(long timeout, TimeUnit unit)  
            throws InterruptedException, ExecutionException, TimeoutException;  
    }  

这种实现线程的方法相对于前两者两说,它可以对线程任务进行部分操作,比如:拿到线程执行的结果,在外界取消任务的执行,在外面查询任务是否之行结束等.

1.2 线程的生命周期

先来张图解:

线程生命周期图

  • 线程的状态分为如下几种:可执行状态(Runnable),执行状态(Running),阻塞状态(Blocked),销毁状态(Dead).其中阻塞状态由于阻塞原因的不同,又分为三种阻塞:等待式阻塞,同步式阻塞,其他阻塞.
  • 等待式阻塞:运行的线程调用了wait()方法.
  • 同步式阻塞:运行的线程在获取对象同步锁时,被别的线程占用,则JVM会把该线程放入锁池.
  • 其他阻塞:由于sleep(),join()或者I/O请求(比如,InputStream.read()),JVM将线程置为阻塞状态,结束后在调用.
  • sleep()wait()方法的同在于:
    1. sleep()方法是Thread的静态方法,而wait()方法是Object类中的成员方法;
    2. sleep()调用的结果是,线程暂停执行;wait()执行的结果是,线程挂起;
    3. 两者最大的区别是:sleep()方法不释放同步锁,而wait()方法是释放同步锁的.sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态;而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备;

下面给出sleep()和wait()的测试代码以及测试结果:

public class TestD {

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable{
        @Override
        public void run(){
            synchronized (TestD.class) {
            System.out.println("enter thread1...");    
            System.out.println("thread1 is waiting...");
            try {
                //调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
                TestD.class.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("thread1 is going on ....");
            System.out.println("thread1 is over!!!");
            }
        }
    }

    private static class Thread2 implements Runnable{
        @Override
        public void run(){
            synchronized (TestD.class) {
                System.out.println("enter thread2....");
                System.out.println("thread2 is sleep....");
                //只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
                TestD.class.notify();
                //==================
                //区别
                //如果我们把代码:TestD.class.notify();给注释掉,即TestD.class调用了wait()方法,但是没有调用notify()
                //方法,则线程永远处于挂起状态。
                try {
                    //sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,
                    //但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
                    //在调用sleep()方法的过程中,线程不会释放对象锁。
                    Thread.sleep(5000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread2 is going on....");
                System.out.println("thread2 is over!!!");
            }
        }
    }
}

测试结果:

enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!
thread1 is going on ....
thread1 is over!!!

相关内容推荐