线程

线程的集中形态,除了Thread之外,还包括AsyncTask、HandlerThread、IntentService,这三者底层实现也是线程,但是他们具有特殊的表现形式,同时在使用上也各有优缺点。

AsyncTask

AsyncTask,是一种轻量级的异步任务类,其维护了两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),它可以在线程池中执行后台任务,然后把执行的进度和最重的结果传递给主线程,并在主线程中更新UI。AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池

其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHeadler用于将执行环境从线程池却换到主线程。

HandlerThread

HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper.

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

步骤

1、创建HandlerThread线程
2、运行线程
3、获取HandlerThread线程中的Looper实例
4、通过Looper实例创建Handler实例,从而使mSubThreadHandler与该线程连接到一起。

应用实例

public class MainActivity extends AppCompatActivity {

    private HandlerThread myHandlerThread ;
    private Handler handler ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //创建一个线程,线程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ;
        //开启一个线程
        myHandlerThread.start();
        //在这个线程中创建一个handler对象
        handler = new Handler( myHandlerThread.getLooper() ){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.d( "handler " , "消息: " + msg.what + "  线程: " + Thread.currentThread().getName()  ) ;

            }
        };

        //在主线程给handler发送消息
        handler.sendEmptyMessage( 1 ) ;

        new Thread(new Runnable() {
            @Override
            public void run() {
             //在子线程给handler发送数据
             handler.sendEmptyMessage( 2 ) ;
            }
        }).start() ;

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //释放资源
        myHandlerThread.quit() ;
    }
}

结果

/com.app D/handler: 消息: 1  线程: handler-thread
/com.app D/handler: 消息: 2  线程: handler-thread

IntentService

(摘自: )

IntentService是一个基础类,用于处理Intent类型的异步任务请求。当客户端调用android.content.Context#startService(Intent)发送请求时,Service服务被启动,且在其内部构建一个工作线程来处理Intent请求。当工作线程执行结束,Service服务会自动停止。IntentService是一个抽象类,用户必须实现一个子类去继承它,且必须实现IntentService里面的抽象方法onHandleIntent来处理异步任务请求。

示例代码:

public class ClientActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //客户端同时发送两个任务到IntentService服务端执行
    public void send(View view) {
        Intent intent = new Intent(this, DownLoadService.class);
        intent.putExtra("key", 1);
        intent.putExtra("value", "the first task1");
        startService(intent);

        Intent intent1 = new Intent(this, DownLoadService.class);
        intent1.putExtra("key", 2);
        intent1.putExtra("value", "the second task2");
        startService(intent1);
    }
}

模拟两个异步任务同时请求,通过Intent实例携带数据启动Service服务。

public class DownLoadService extends IntentService {

    public static final String TAG = "DownLoadService";
    //重写默认的构造方法
    public DownLoadService() {
        super("DownLoadService");
    }

    //在后台线程执行 -- 子线程
    @Override
    protected void onHandleIntent(Intent intent) {
        int key = intent.getIntExtra("key", 0);
        String value = intent.getStringExtra("value");
        switch (key) {
            case 1:
                //模拟耗时任务1
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            case 2:
                //模拟耗时任务1
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }

        Log.e(TAG, "\nthe current time is: " + System.currentTimeMillis()/1000
                + "\nthe Thread id is " + Thread.currentThread().getId()
                + "\nthe current task is " + value);
    }
}

DownLoadService子类继承IntentService类,然后实现onHandleIntent抽象方法进行处理Intent请求的异步任务。在服务端DownLoadService类中,我们并没有创建Thread线程去执行异步耗时任务请求。所有的异步耗时任务都是在onHandleIntent抽象方法中实现了。言外之意是IntentService类内部已经帮开发者搭建好了一个异步任务处理器,用户只需实现其中的onHandleIntent抽象方法去处理异步任务即可,从而让开发者更加简单方便的使用IntentService处理后台异步任务请求。那么IntentService内部是怎么搭建异步任务处理器的呢?我们不妨查看源码来窥探个究竟。

IntentService源码分析

IntentService构造方法
/**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

分析:该构造方法需在子类中调用,用于创建一个IntentService对象。参数name用于定义工作线程的名称,仅仅用于调式作用。我们知道Service服务的生命周期是从onCreate方法开始的。那么就来看看IntentService#onCreate方法吧。

IntentService#onCreate方法
public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
 }

该方法首先利用HandlerThread类创建了一个循环的工作线程thread,然后将工作线程中的Looper对象作为参数来创建ServiceHandler消息执行者。上文分析可知,HandlerThread+Handler构建成了一个带有消息循环机制的异步任务处理机制。因此开发者就可以将异步任务封装成消息的形式发送到工作线程中去执行了。Service服务生命周期第二步执行IntentService#onStartCommand方法。

IntentService#onStartCommand方法
/**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

分析:在IntentService子类中你无需重写该方法。然后你需要重写onHandlerIntent方法,系统会在IntentService接受一个请求开始调用该方法。我们看到在该方法中仅仅是调用了onStart方法而已,跟踪代码:

@Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

分析:该方法中通过mServiceHandler获得一个消息对象msg,然后将startId作为该消息的消息码,将异步任务请求intent作为消息内容封装成一个消息msg发送到mServiceHandler消息执行者中去处理,那么我们来看看mServiceHandler的实现吧。

private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
           super(looper);
       }

       @Override
       public void handleMessage(Message msg) {
           onHandleIntent((Intent)msg.obj);
           stopSelf(msg.arg1);
       }
}

分析:实现也比较简单,ServiceHandler是IntentService的内部类,在重写消息处理方法handlerMessage里面调用了onHandlerIntent抽象方法去处理异步任务intent的请求,当异步任务请求结束之后,调用stopSelf方法自动结束IntentService服务。由前面HandlerThread的分析可知,此处handleMessage方法是在子线程中调用的,因此我们子类重写的onHandlerIntent也是在子线程中实现的。我们来看看onHandlerIntent方法:

/**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);

分析:该方法用于处理intent异步任务请求,在工作线程中调用该方法。每一个时刻只能处理一个intent请求,当同时又多个intent请求时,也就是客户端同时多次调用Content#startService方法启动同一个服务时,其他的intent请求会暂时被挂起,直到前面的intent异步任务请求处理完成才会处理下一个intent请求。直到所有的intent请求结束之后,IntentService服务会调用stopSelf停止当前服务。也就是当intent异步任务处理结束之后,对应的IntentService服务会自动销毁,进而调用IntentService#onDestroy方法:

@Override
 public void onDestroy() {
      mServiceLooper.quit();
 }

该方法中调用HandlerThread工作线程中Looper对象的quit方法让当前工作线程HandlerThread退出当前Looper循环,进而结束线程,结束当前IntentService服务。到此,整个IntentService服务结束,适合一次性的后台任务。

Android中的线程池

优点概况:
1、重用线程池中的线程,避免因为线程的创建和销毁所带来的性能的开销。
2、能有限控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
3、能够对线程进行简单的管理,并提供定时执行以及指定间隔以及指定间隔循环执行等功能。

(以下摘自: )

线程池的创建

Android 中的线程池的概念来源于 Java 中的 Executor ,Executor 是一个接口,真正的线程池的实现为 ThreadPoolExecutor,ThreadPoolExecutor 提供了一些列的参数来配置线程池,通过不同的参数可以创建功能特性不同的线程池。我们要创建一个线程池只需要 new ThreadPoolExecutor(…) 就可以创建一个线程池,但我们如此创建线程池的话,需要配置一些参数,非常麻烦,同时 Google 官方也不推荐使用这种方式来创建线程池,而是推荐使用 Executors 的工厂方法来创建线程池。但 Executors 的工厂方法创建的线程池也是直接或间接通过配置 ThreadPoolExecutor 参数来实现的,因此有必要先了解 ThreadPoolExecutor 的配置参数。

ThreadPoolExecutor

ThreadPoolExecutor 的构造方法提供了一些列的参数来配置线程池,先来了解一下 ThreadPoolExecutor 的构造方法中各个参数的含义,这些参数将直接影响到线程池的功能特性。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize, 
                              long keepAliveTime, 
                              TimeUnit unit, 
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory, 
                              RejectedExecutionHandler handler) {...
    }
corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。但如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true ,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔是由 keepAliveTime 所指定,当等待时间超过 keepAliveTime 所指定的时长后,核心线程就会被终止。

maximumPoolSize

线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true 时,keepAliveTime 同样会作用于核心线程。

unit

用于指定 keepAliveTime 参数的时间单位,这是一个枚举,常用的有 TimeUnit .MILLISECONDS 和 TimeUnit .SECONDS。

workQueue

线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。

threadFactory

线程工厂,为线程池提供创建新的线程的功能。threadFactory 是一个接口,它只有一个方法: public abstract Thread newThread (Runnable r);

RejectedExecutionHandler

当线程池无法执行先任务是,这可能是由于任务队列已满或者是无法成功执行任务,这个时候ThreadPoolExecutor会调用Handler的rejectedExecution方法来通知调用者,默认情况下rejectedExecutor方法会直接抛出一个RejectedExecutionException .

ThreadPoolExecutor 执行任务是大致遵循如下规则:

1、如果线程池中的线程数量未达到和弦线程的数量,那么会直接启动一个核心线程来执行任务。

2、如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。

3、如果在步骤 2 中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定最大值,那么会立刻启动一个非核心线程来执行任务。

4、如果步骤 3 中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler 的rejectedExecution方法来通知调用者。

AsyncTask 中线程池的配置情况

AsyncTask是一个可以借鉴的封装线程池的方式

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
 
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating the CPU with background work
    
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    
    private static final int KEEP_ALIVE_SECONDS = 30;

	private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

	private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

   /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

● 核心线程数最少有2个线程,最多有4个线程
● 线程池的最大线程数为CPU核心数的 2 倍 + 1;
● 核心线程有超时机制,非核心线程在闲置时的超时时间为30秒
● 任务队列的容量为128

线程池的分类

Android 中最常见的四类具有不同功能特性的线程池,他们都是直接或间接的通过配置ThreadPoolExecutor来实现自己的功能特性,这四类线程池分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool 以及 SingleThreadExecutor.

FixedThreadPool

通过Executors 的newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于 FixedThreadPool 只有核心线程并且这些核心线程不会被回收,这意味着它能快速的响应外界的请求。 newFixedThreadPool 方法的实现如下,可以发现 FixedThreadPool 中只有核心线程并且这些核心线程没有超时机制另外任务队列也是没有大小限制

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
  }

CachedThreadPool

通过 Executors.newCachedThreadPool 来创建,它是一个线程数量不定的线程池,它只有非核心线程,并且最大线程数为 Integer.MAX_VALUE ,由于 Integer.MAX_VALUE 是一个很大的数,实际上就相当于最大核心线程数可以任意大。当线程池中的线程都处于活动状态时,当有新任务过来时,线程池就会创建新的线程来处理新任务,否则就会利用空闲的线程来处理。线程池中的空闲线程都有超时机制,这个超时时长为 60 秒,超过 60 秒闲置线程就会被回收。SynchronousQueue 队列相当于一个空队列,这就导致任何任务都会立即执行。这类线程池比较适合执行大量的耗时较少的任务当整个线程池都处于闲置状态时,线程池中的线程都会因超时而被停止,这个时候CacheThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

ScheduledThreadPool

通过 Executors.newScheduledThreadPool 来创建,它的核心数量是固定的,非核心线程没有限制,并且当非核心线程闲置时会被立即回收,ScheduledThreadPool 这类线程池主要用于执行定时任务和具有固定周期的重复任务

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

SingleThreadExecutor

通过 Executors.newSingleThreadExecutor 来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor 的意义在于统一所有的外界任务到一个线程中,这使得这些任务之间不需要处理线程同步的问题。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

线程池 ThreadPoolExecutor 的使用

使用线程池,其中涉及到一个极其重要的方法,即:

execute(Runnable command)
Executes the given task sometime in the future.

该方法意为执行给定的任务,该任务处理可能在新的线程、已入池的线程或者正调用的线程,这由 ThreadPoolExecutor 的实现决定。

1、newFixedThreadPool 创建一个固定线程数量的线程池,示例为:

@Override
    public void onClick(View v) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                   Log.v(TAG, "线程:"+threadName+",正在执行第" + index + "个任务");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

在上述代码中,创建了线程数量固定为 3 的线程池,该线程池支持的最大线程也为 3 ,而我们创建了 10 个任务让它处理,通过 log 分析,执行的情况是先执行前 3 个任务,后面 7 个任务都进入任务队列进行等待,执行完前 3 个任务后,再通过 FIFO 的方式从任务队列中取任务执行,直到所有任务都执行完毕。

android线程管理工具类_线程池


newSingleThreadExecutor 创建一个只有一个线程数的线程池。通过 log 分析,每次线程池只执行一个任务,其余的任务都将进入任务队列进行等待。

@Override
    public void onClick(View v) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                    Log.v(TAG, "线程:"+threadName+",正在执行第" + index + "个任务");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

代码改动不大,只是改动了 ThreadPoolExecutor 的实现方式,任务都是一个一个的执行,并且都是同一个线程。

android线程管理工具类_ide_02

newCachedThreadPool 创建一个可以根据实际情况调整线程池中线程数量的线程池。

@Override
    public void onClick(View v) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                    Log.v(TAG, "线程:"+threadName+",正在执行第" + index + "个任务");
                    try {
                        long time = index * 500;
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

为了体现该线程池可以自动根据实际情况进行线程的重用,而不是一味的创建新的线程去处理任务,设置了每隔 1s 去提交一个新任务,这个新任务执行的时间也是动态变化的。

android线程管理工具类_android线程管理工具类_03

通过 log 可以看出,新增的任务都会被立即执行。

newScheduledThreadPool 创建一个可以定时或周期性执行任务的线程池。

@Override
    public void onClick(View v) {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
        threadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                Log.v(TAG, "线程:" + threadName + ",正在执行");
            }
        }, 2, 1, TimeUnit.SECONDS);
    }

延迟 2 秒后,每隔 1 秒执行一次该任务

android线程管理工具类_异步任务_04

从 log 日志可以看出 ScheduledThreadPool 是 4 个线程池里面唯一一个个有延迟执行和周期重复执行的线程池。

总结

  1. FixedThreadPool 只有核心线程,并且数量是固定的,也不会被回收,能更快地响应外界请求。
  2. CachedThreadPool 只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程处理任务,任何任务都会被立即执行。
  3. SingleThreadPool 只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。
  4. ScheduledThreadPool 核心线程数固定,非核心线程数没有限制,主要用于执行定时任务以及有固定周期的重复任务

Android 自定义线程池

(以下摘自: )

BlockingQueue 的接口队列

在多数情况下,我们构建线程池主要是通过 Executors 的工厂方法来创建线程池,但 Executors 的工厂方法创建的线程池也是直接或间接通过配置 ThreadPoolExecutor 参数来实现的。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize, 
                              long keepAliveTime, 
                              TimeUnit unit, 
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory, 
                              RejectedExecutionHandler handler) {...
    }

在上面的参数中理解起来都比较简单,但 workQueue 要多说明一下。workQueue 是一个 BlockingQueue 对象,它是一个特殊的队列,当我们从 BlockingQueue 中取数据时,如果 BlockingQueue 是空的,则取数据的操作会进入到阻塞状态,当 BlockingQueue 中有了新数据时,这个取数据的操作又会被重新唤醒。同理,如果 BlockingQueue 中的数据已经满了,往 BlockingQueue 中存数据的操作又会进入阻塞状态,直到 BlockingQueue 中又有新的空间,存数据的操作又会被重新唤醒。它的泛型限定它是用来存放 Runnable 对象的。不同的线程池它的任务队列实现是不一样的,所以保证不同线程池不同功能的核心就是这个 workQueue 的实现了,通过 Executors 的工厂方法创建的线程池传入的 workQueue 也是不一样的。接下来就来说明 BlockingQueue 的多种不同的实现类。

1、ArrayBlockingQueue:这个表示一个规定了大小的BlockingQueue,ArrayBlockingQueue 的构造函数接受一个 int 类型的数据,该数据表示BlockingQueue 的大小,存储在 ArrayBlockingQueue 中的元素按照 FIFO(先进先出)的方式来进行存取

2、LinkedBlockingQueue:这个表示一个大小不确定的 BlockingQueue,在LinkedBlockingQueue 的构造方法中可以传一个 int 类型的数据,这样创建出来的 LinkedBlockingQueue 是有大小的,也可以不传,不传的话,LinkedBlockingQueue 的大小就为 Integer.MAX_VALUE

3、PriorityBlockingQueue:这个队列和 LinkedBlockingQueue 类似,不同的是PriorityBlockingQueue 中的元素不是按照 FIFO 来排序的,而是按照元素的Comparator 来决定存取顺序的(这个功能也反映了存入 PriorityBlockingQueue 中的数据必须实现了 Comparator 接口)。

4、SynchronousQueue:这个是同步 Queue,属于线程安全的 BlockingQueue的一种,在 SynchronousQueue 中,生产者线程的插入操作必须要等待消费者线程的移除操作,Synchronous 内部没有数据缓存空间,因此我们无法对 SynchronousQueue进行读取或者遍历其中的数据,元素只有在你试图取走的时候才有可能存在。我们可以理解为生产者和消费者互相等待,等到对方之后然后再一起离开。

了解了 BlockingQueue 的种类,再来看看各种不同线程池分别传入的 workQueue 的类型:

1、newFixedThreadPool()—>LinkedBlockingQueue
2、newSingleThreadExecutor()—>LinkedBlockingQueue
3、newCachedThreadPool()—>SynchronousQueue
4、newScheduledThreadPool()—>DelayedWorkQueue
自定义线程池 ThreadPoolExecutor

一般来说,Java 内置的线程池已经够我们使用了,不过有时候我们也可以根据需求来自定义线程池,而要自定义不同功能的线程池,归根结底还是要根据 BlockingQueue 的实现,所以我们要自定义线程池,就必须从 BlockingQueue 着手,而上面说了 BlockingQueue 的实现类有多个,那么我们这次就选用 PriorityBlockingQueue 来实现一个按任务的优先级来处理的线程池。

1、首先我们创建一个基于 PriorityBlockingQueue 实现的线程池.

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());

2、创建一个实现 Runnable 接口的类,并向外提供一个抽象方法供我们实现自定义功能,并实现 Comparable 接口,实现这个接口主要就是进行优先级的比较.

public abstract class PriorityRunnable implements Comparable<PriorityRunnable> ,Runnable{

    private int priority;

    public PriorityRunnable(int priority) {
        if (priority < 0)
            throw new IllegalArgumentException();
        this.priority = priority;
    }

    @Override
    public int compareTo(PriorityRunnable another) {
        int my = this.getPriority();
        int other = another.getPriority();
        return my < other ? 1 : my > other ? -1 : 0;
    }

    @Override
    public void run() {
        doSth();
    }

    public abstract void doSth();

    public int getPriority() {
        return priority;
    }
}

3、使用我们自己的 PriorityRunnable 提交任务,整体代码如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());

for (int i = 0; i < 30; i++) {
	final int priority = i;
	PriorityRunnable runnable = new PriorityRunnable(priority) {
		@Override
		public void doSth() {
			String threadName = Thread.currentThread().getName();
			Log.v(TAG, "线程:" + threadName + ",正在执行优先级为:" + priority + "的任务");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
	executor.execute(runnable);
}

我们看下刚刚自定义的线程池是否达到了我们想要的功能,即根据任务的优先级进行优先处理任务,效果如下:

android线程管理工具类_android线程管理工具类_05

android线程管理工具类_线程_06

可以从执行结果中看出,由于核心线程数设置为 3,刚开始时,系统有 3 个空闲线程,所以无须使用任务队列,而是直接运行前三个任务,而后面再提交任务时由于当前没有空闲线程所以加入任务队列中进行等待,此时,由于我们的任务队列实现是由 PriorityBlockingQueue 实现的,所以进行等待的任务会经过优先级判断,优先级高的放在队列前面先处理。从效果图中也可以看到后面的任务是先执行优先级高的任务,然后依次递减。

优先级线程池的优点

从上面我们可以得知,创建一个优先级线程池非常有用,它可以在线程池中线程数量不足或系统资源紧张时,优先处理我们想要先处理的任务,而优先级低的则放到后面再处理,这极大改善了系统默认线程池以 FIFO 方式处理任务的不灵活.(这里表达的优先级的高低是单指当前的案例中的 priority 的值,这里的有优先级是可控的,由compareTo的返回值来控制)(源码分析: )

情况一、如果当前 priority 的值大于形参中 priority 值 compareTo 返回 1的话,反之返回 -1的话。出队列的方式是优先执行 / 输出 priority 值小的任务 / 对象。情况二、如果当前 priority的值大于形参中的 priority值 compareTo返回 -1 的话,反之返回1的话。出队列的方式是优先执行或输出 priority 值大的任务或对象。该例中,是情况二。

android线程管理工具类_线程_07


扩展线程池ThreadPoolExecutor

除了内置的功能外,ThreadPoolExecutor 也向外提供了三个接口供我们自己扩展满足我们需求的线程池,这三个接口分别是:

  1. beforeExecute() - 任务执行前执行的方法
  2. afterExecute() -任务执行结束后执行的方法
  3. terminated() -线程池关闭后执行的方法

这三个方法在 ThreadPoolExecutor 内部都没有实现,我们也可以通过自定义 ThreadPoolExecutor 来实现这个功能。

public class MyThreadPool extends ThreadPoolExecutor {

    private static final String TAG = "Bradley" ;

    public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        Log.d(TAG, "beforeExecute: 开始执行任务!");
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        Log.d(TAG, "beforeExecute: 任务执行结束!");
    }

    @Override
    protected void terminated() {
        super.terminated();
        Log.d(TAG, "terminated: 线程池关闭!");
    }
}

而运行后的结果则是,这正符合刚刚说的。

android线程管理工具类_线程池_08


优化线程池 ThreadPoolExecutor

虽说线程池极大改善了系统的性能,不过创建线程池也是需要资源的,所以线程池内线程数量的大小也会影响系统的性能,大了反而浪费资源,小了反而影响系统的吞吐量,所以我们创建线程池需要把握一个度才能合理的发挥它的优点,通常来说我们要考虑的因素有 CPU 的数量、内存的大小、并发请求的数量等因素,按需调整。
通常核心线程数可以设为CPU数量+1,而最大线程数可以设为CPU的数量*2+1。
轻量级的配置可参考 AsyncTask线程池的配置情况。

获取CPU数量的方法为:

Runtime.getRuntime().availableProcessors();

shutdown()和shutdownNow()的区别

关于线程池的停止,ExecutorService 为我们提供了两个方法:shutdown 和 shutdownNow,这两个方法各有不同,可以根据实际需求方便的运用,如下:

  1. shutdown() 方法在终止前允许执行以前提交的任务。
  2. shutdownNow() 方法则是阻止正在任务队列中等待任务的启动并试图停止当前正在执行的任务。

线程池到此介绍完了。

Handler

分析线程Android 中不得不提到 Handler的运行机制,也就是Android的消息机制。Handler的运行需要底层的MessageQueue 和 Looper的支撑,MessageQueue的中文翻译是消息队列,它内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是他的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper称作消息循环,MessageQueue负责存储消息,Looper负责循环消息,它会以无线循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。Looper中还有一个特殊的概念,那就是ThreadLocal,ThreadLocal并不是线程,他的作用是可以在每个线程中存储数据。Handler在创建的时候会采用当前的Looper来构造消息循环系统,那么Handler内部如何获取到当前线程的Looper的呢,这就要是使用ThreadLocal了,ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是,线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper,UI线程中,他就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。

下面先了解一下 ThreadLocal 、MessageQueue、Looper 的工作原理。

ThreadLocal的工作原理



ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说无法获取到数据。一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal

其一、比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取,如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个类似于LooperManager的类了,但是系统并没有这么做而是选择了ThreadLocal,这就是ThreadLocal的好处。

其二、 ThreadLocal另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,我们又需要监听器能够贯穿整个线程的执行过程,这个时候可以怎么做呢?其实就可以采用ThreadLocal,采用ThreadLocal可以让监听器作为线程内的全局对象而存在在线程内部只要通过get方法就可以获取到监听器

介绍了那么多ThreadLocal的知识,可能还是有点抽象,下面通过实际的例子为大家演示ThreadLocal的真正含义。首先定义一个ThreadLocal对象,这里选择Boolean类型的,如下所示:

private ThreadLocal<Boolean>mBooleanThreadLocal = new ThreadLocal<Boolean>();

然后分别在主线程、子线程1和子线程2中设置和访问它的值,代码如下所示:

mBooleanThreadLocal.set(true);
Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
 
new Thread("Thread#1") {
	@Override
	public void run() {
		mBooleanThreadLocal.set(false);
		Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
	};
}.start();
 
new Thread("Thread#2") {
	@Override
	public void run() {
		Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
	};
}.start();

在上面的代码中,在主线程中设置mBooleanThreadLocal的值为true,在子线程1中设置mBooleanThreadLocal的值为false,在子线程2中不设置mBooleanThreadLocal的值,然后分别在3个线程中通过get方法去mBooleanThreadLocal的值,根据前面对ThreadLocal的描述,这个时候,主线程中应该是true,子线程1中应该是false,而子线程2中由于没有设置值,所以应该是null,安装并运行程序,日志如下所示:

D/TestActivity(8676):[Thread#main]mBooleanThreadLocal=true

D/TestActivity(8676):[Thread#1]mBooleanThreadLocal=false

D/TestActivity(8676):[Thread#2]mBooleanThreadLocal=null

从上面日志可以看出,虽然在不同线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal来获取到的值却是不一样的,这就是ThreadLocal的奇妙之处。结合这这个例子然后再看一遍前面对ThreadLocal的两个使用场景的理论分析,大家应该就能比较好地理解ThreadLocal的使用方法了。ThreadLocal之所以有这么奇妙的效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值,很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且彼此互不干扰。

对ThreadLocal的使用方法和工作过程做了一个介绍后,下面分析下ThreadLocal的内部实现, ThreadLocal是一个泛型类,它的定义为public class ThreadLocal,只要弄清楚ThreadLocal的get和set方法就可以明白它的工作原理

首先看ThreadLocal的set方法,如下所示:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}

Thread 中的属性

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
     
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal 里的静态类

static class ThreadLocalMap {

   // 静态内部类中的静态类
	static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
	......
	/**
     * The table, resized as necessary.
     * table.length MUST always be a power of two.
     */
    private Entry[] table;
    ......
    // 创建ThreadLocalMap对象
     ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }	
	......
	// 从表中取值
	private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
     }

  	 // 存储对象到表中
 	 private void set(ThreadLocal<?> key, Object value) {

        // We don't use a fast path as with get() because it is at
        // least as common to use set() to create new entries as
        // it is to replace existing ones, in which case, a fast
        // path would fail more often than not.

        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);

        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();

            if (k == key) {
                e.value = value;
                return;
            }

            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }

        tab[i] = new Entry(key, value);
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
     }
}

ThreadLocal 中 get()方法:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
}

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

看到以上代码就一目了然了,其中set方法把存储对象value存储到弱引用数组table里面。不同的线程Thread持有不同的ThreadLocalMap 当然其存储的值也就不一样了,get方法是直接从table里面取值。

这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据,理解ThreadLocal的实现方式有助于理解Looper的工作原理。

MessageQueue的工作原理

MessageQueue 主要包括两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage 和 next ,其中enqueueMessage 的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除。MessageQueue 实际上是以单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
}

从enqueueMessage 的实现来看,他的主要操作其实就是单链表的插入操作,这里不做过多的解释,下面看一下next 方法的实现,如下:

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
}

这里可以发现next 方法是一个无线循环的方法,如果消息队列中没有消息,那么next 方法会一直阻塞在这里,当有新消息到来时,next 方法会返回这条消息并将其从单链表中移除。

Looper的工作原理

Looper 在Android 的消息机制中扮演者消息循环的角色,具体来说就是他会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。其构造方法如下所示:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为一个线程创建Looper呢,其实很简单,通过Looper.prepare() 即可为当前线程创建一个Looper,接着通过Looper.loop() 来开启消息循环。如下:

new Thread("Thread#2") {
	@Override
	public void run(){
		Looper.prepare();
		Handler handler = new Handler();
		Looper.looper();
    }
}

Looper 除了prepare方法外,还提供了 prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的,由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper 方法,通过它可以在任何地方获取到主线程的Looper。Looper也是可以退出的,Looper提供了quit 和 quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出后通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此不需要线程的时候终止Looper

Looper的prepare方法如下:

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
}

private native static long nativeInit();

由以上方法可以看出,多次创建Looper会抛异常,一个线程只允许有一个Looper.创建Looper的过程就是 先创建消息队列,并将Looper对象封存到ThreadLocal中,这就对应了前面的例子,每个线程对应不同的Looper,并互不干扰地存储和修改数据。

再看一下Looper开启循环的过程,如下:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

Looper的loop方法的工作过程也比较好理解,Looper方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。loop方法会调用MessageQueue的next方法来获取新消息,而next实施一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功的将代码逻辑切换到指定的线程中去执行了。

Handler 的工作原理
Handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post的一系列方法以及send的意思咧方法来实现,post的一系列方法最终是通过send的一系列方法来实现的。发送一条消息的典型过程如下:

/**
* Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(Message msg)
{
     return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
}


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}

// MessageQueue 中的方法
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
}

可以发现,Handler发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入了消息处理的阶段。dispatchMessage 的实现如下:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        // callback是msg中的一个字段,是一个Runnable对象,当通过handler.post方法发送一个runnable的时候就会被封装到这个msg中
        if (msg.callback != null) {
           // 处理Runnable任务
            handleCallback(msg);
        } else {
           // 这里是 处理 Handler构造方法中的Callback
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

Handler处理消息的过程如下:

首先检查Message的callback是否为null,不为null就通过handleCallback来处理消息。Message的callback是一个Runnable对象,实际上就是Handler的post方法传递的Runnable参数。handleCallback的逻辑如下:

private static void handleCallback(Message message) {
        message.callback.run();
}

其次,检查mCallback 是否为null,不为null就调用mCallback 的handleMessage方法来处理消息。Callback是一个接口。定义如下:

/**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

通过Callback可以采用如下方式来创建Handler对象:Handler handler = new Handler(callback) 那么Callback的意思是什么呢,源码注释做了说明:可以用来创建一个Handler的实例但并不需要还剩Handler的子类。在日常开发中创建Handler最常见的方式就是派生一个Handler的子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种使用Handler的方式,当我们不想派生子类时,就可以通过Callback来实现。

注意,在通过Callback创建Handler对象的时候,如果接口中的handleMessage 返回true 则,不再执行 Handler中的HandleMessage,返回false 则还会执行。

最后通过一个流程图来总结一下:

android线程管理工具类_线程_09


线程逻辑如下:

android线程管理工具类_异步任务_10

线程的同步问题

(以下内容部分摘自: )

线程状态

1、wait()。使一个线程处于等待状态,并且释放所有持有对象的lock锁,直到notify()/notifyAll()被唤醒后放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)。

2、sleep()。使一个线程处于睡眠状态,是一个静态方法,调用此方法要捕捉Interrupted异常,醒来后进入runnable状态,等待JVM调度。

3、notify()。使一个等待状态的线程唤醒,注意并不能确切唤醒等待状态线程,是由JVM决定且不按优先级。

4、allnotify()。使所有等待状态的线程唤醒,注意并不是给所有线程上锁,而是让它们竞争。

5、join()。使一个线程中断,IO完成会回到Runnable状态,等待JVM的调度。

6、Synchronized()。使Running状态的线程加同步锁使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。注意:当线程在runnable状态时是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

基础概念

1、 并行。多个cpu实例或多台机器同时执行一段代码,是真正的同时。

2、并发。通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。

3、线程安全。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。线程不安全就意味着线程的调度顺序会影响最终结果,比如某段代码不加事务去并发访问。

4、线程同步。指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如某段代码加入@synchronized关键字。线程安全的优先级高于性能优化。

5、原子性。一个操作或者一系列操作,要么全部执行要么全部不执行。数据库中的“事物”就是个典型的院子操作。

6、可见性。当一个线程修改了共享属性的值,其它线程能立刻看到共享属性值的更改。比如JMM分为主存和工作内存,共享属性的修改过程是在主存中读取并复制到工作内存中,在工作内存中修改完成之后,再刷新主存中的值。若线程A在工作内存中修改完成但还来得及刷新主存中的值,这时线程B访问该属性的值仍是旧值。这样可见性就没法保证。

7、有序性。程序运行时代码逻辑的顺序在实际执行中不一定有序,为了提高性能,编译器和处理器都会对代码进行重新排序。前提是,重新排序的结果要和单线程执行程序顺序一致。

Synchronized同步

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。补充: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。

1、方法同步。给方法增加synchronized修饰符就可以成为同步方法,可以是静态方法、非静态方法,但不能是抽象方法、接口方法。小示例:

public synchronized void aMethod() { 
    // do something 
} 
 
public static synchronized void anotherMethod() { 
    // do something 
}

详解:线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

使用方法同步保护共享数据。示例:

public class ThreadTest implements Runnable{
 
	public synchronized void run(){
	  for(int i=0;i<10;i++) {
	    System.out.print(" " + i);
	  }
	}
	 
	public static void main(String[] args) {
	  Runnable r1 = new ThreadTest(); 
	  Runnable r2 = new ThreadTest();
	  Thread t1 = new Thread(r1);
	  Thread t2 = new Thread(r2);
	  t1.start();
	  t2.start();
	}
}

代码中可见,run()被加上了synchronized 关键字,但保护的并不是共享数据。因为程序中两个线程对象 t1、t2 其实是另外两个线程对象 r1、r2 的线程,这个听起来绕,但是一眼你就能看明白;因为不同的线程对象的数据是不同的,即 r1,r2 有各自的run()方法,所以输出结果就无法预知。这时使用 synchronized 关键字可以让某个时刻只有一个线程可以访问该对象synchronized数据。每个对象都有一个“锁标志”,当这个对象的一个线程访问这个对象的某个synchronized 数据时,这个对象的所有被synchronized 修饰的数据将被上锁(因为“锁标志”被当前线程拿走了),只有当前线程访问完它要访问的synchronized 数据时,当前线程才会释放“锁标志”,这样同一个对象的其它线程才有机会访问synchronized 数据。

接下来,我们把 r2 给注释掉, 即只保留一个 r 对象。如下:

public class ThreadTest implements Runnable{
	 
	public synchronized void run(){
	  for(int i=0;i<10;i++){
	    System.out.print(" " + i);
	  }
	}
	 
	public static void main(String[] args){
	  Runnable r = new ThreadTest();
	  Thread t1 = new Thread(r);
	  Thread t2 = new Thread(r);
	  t1.start();
	  t2.start();
	}
}

示例详解:

如果你运行1000 次这个程序,它的输出结果也一定每次都是:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。因为这里的synchronized 保护的是共享数据。t1,t2 是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run()方法时,由于run()受synchronized保护,所以同一个对象的其他线程(t2)无法访问synchronized 方法(run 方法)。只有当t1执行完后t2 才有机会执行。

2、块同步:同步块是通过锁定一个指定的对象,来对块中的代码进行同步;同步方法和同步块之间的相互制约只限于同一个对象之间,静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例没有关系。如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块。

使用块同步,示例:

public class ThreadTest implements Runnable{
   public void run(){
      synchronized(this){  //与上面示例不同于关键字使用
           for(int i=0;i<10;i++){
               System.out.print(" " + i);
           }
      } 
   }
   public static void main(String[] args){
       Runnable r = new ThreadTest();
       Thread t1 = new Thread(r);
       Thread t2 = new Thread(r);
       t1.start();
       t2.start();
   }
}

示例详解:

这个与上面示例的运行结果也一样的。这里是把保护范围缩到最小,this 代表 ‘这个对象’ 。没有必要把整个run()保护起来,run()中的代码只有一个for循环,所以只要保护for 循环就可以了。

再看一个示例:

public class ThreadTest implements Runnable{
 
	public void run(){
	  for(int k=0;k<5;k++){
	    System.out.println(Thread.currentThread().getName()+ " : for loop : " + k);
	  }
	 
	synchronized(this){
	  for(int k=0;k<5;k++) {
	    System.out.println(Thread.currentThread().getName()+ " : synchronized for loop : " + k);
	  }} 
	}
 
	public static void main(String[] args){
	  Runnable r = new ThreadTest();
	  Thread t1 = new Thread(r,"t1_name");
	  Thread t2 = new Thread(r,"t2_name");
	  t1.start();
	  t2.start();
	} 
}

//运行结果:

t1_name : for loop : 0
t1_name : for loop : 1
t1_name : for loop : 2
t2_name : for loop : 0
t1_name : for loop : 3
t2_name : for loop : 1
t1_name : for loop : 4
t2_name : for loop : 2
t1_name : synchronized for loop : 0
t2_name : for loop : 3
t1_name : synchronized for loop : 1
t2_name : for loop : 4
t1_name : synchronized for loop : 2
t1_name : synchronized for loop : 3
t1_name : synchronized for loop : 4
t2_name : synchronized for loop : 0
t2_name : synchronized for loop : 1
t2_name : synchronized for loop : 2
t2_name : synchronized for loop : 3
t2_name : synchronized for loop : 4

示例详解:

第一个for 循环没有受synchronized 保护。对于第一个for 循环,t1,t2 可以同时访问。运行结果表明t1 执行到了k=2 时,t2 开始执行了。t1 首先执行完了第一个for 循环,此时t2还没有执行完第一个for 循环(t2 刚执行到k=2)。t1 开始执行第二个for 循环,当t1的第二个for 循环执行到k=1 时,t2 的第一个for 循环执行完了。t2 想开始执行第二个for 循环,但由于t1 首先执行了第二个for 循环,这个对象的锁标志自然在t1 手中(synchronized 方法的执行权也就落到了t1 手中),在t1 没执行完第二个for 循环的时候,它是不会释放锁标志的。所以t2 必须等到t1 执行完第二个for 循环后,它才可以执行第二个for 循环。

总结几点

1、synchronized 锁住的是某个对象的实例,无关子类父类,多个线程同时拥有一个对象,才能起到同步的作用。不同的对象实例同一个线程或者不同的线程执行,synchronized 不起作用。

2、synchronized 作用于某个方法时,其方法内也调用了有synchronized 同步方法时,子方法的同步不起作用。

Volatile 同步

volatile只是通知底层计算时,CPU检查内存数据,而不是让一个变量在多个线程中同步。

a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值 
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

只需在account前面加上volatile修饰,即可实现线程同步。

//只给出要修改的代码,其余代码与上同
 class Bank {
     //需要同步的变量加上volatile
     private volatile int account = 100;

     public int getAccount() {
         return account;
     }
     
     //这里不再需要synchronized 
     public void save(int money) {
         account += money;
     }
 }

多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。 用final域,有锁保护的域和volatile域可以避免非同步的问题。

重入锁同步

在 JavaSE5.0中 新增了一个 java.util.concurrent 包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。 ReenreantLock类的常用方法有:

ReentrantLock() : 创建一个ReentrantLock实例 
lock() : 获得锁 
unlock() : 释放锁

注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用 例如:

class Bank {
    private int account = 100;
    //需要声明这个锁
    private Lock lock = new ReentrantLock();
    public int getAccount() {
        return account;
    }
    //这里不再需要synchronized 
    public void save(int money) {
        lock.lock();
        try{
            account += money;
        }finally{
            lock.unlock();
        }
    }
}

关于Lock对象和synchronized关键字的选择:

1、最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。

2、如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码

3、如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁 。