編輯:關於android開發
線程通信、ActivityThread及Thread類是理解Android線程管理的關鍵。
線程,作為CPU調度資源的基本單位,在Android等針對嵌入式設備的操作系統中,有著非常重要和基礎的作用。本小節主要從以下三個方面進行分析:
線程是CPU資源調度的基本單位,屬於抽象范疇,Java通過Thread類完成線程管理。Thread類本質其實是“可執行代碼”,其實現了Runnable接口,而Runnable接口唯一的方法就是run()。
public class Thread implements Runnable { …… }
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(); }
從注釋可以看出,調用Thread的start()方法就是間接調用Runnable接口的run()方法。
public synchronized void start() { checkNotStarted(); hasBeenStarted = true; VMThread.create(this, stackSize); }
start()方法中VMThread.create(this, stackSize)是真正創建CPU線程的地方,換句話說,只有調用start()後的Thread才真正創建CPU線程,而新創建的線程中運行的就是Runnable接口的run()方法。
線程通信、同步、協作是多線程編程中常見的問題。線程協作通常是采用線程休眠及喚醒來實現的,線程的休眠通過等待某個對象的鎖(monitor)實現(wait()方法),當其他線程調用該對象的notify()方法時,該線程就被喚醒。該對象實現在線程間數據傳遞,多個線程通過該對象實現協作。
線程協作的經典例子是Java設計模式中的“生產者-消費者模式”,生產者不斷往緩沖區寫入數據,消費者從緩沖區中取出數據進行消費。在實現上,生產者與消費者分別繼承Thread,緩沖區采用優先級隊列PriorityQueue來模擬。生產者將數據放入緩沖區的前提是緩沖區有剩余空間,消費者從緩沖區中取出數據的前提是緩沖區中有數據,因此,這就涉及到生成者線程與消費者線程之間的協作。下面通過代碼簡要說明下。
import java.util.PriorityQueue; public class TestWait { private int size = 5; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(size); public static void main(String[] args) { TestWait test = new TestWait(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread { @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == 0) { try { System.out.println("隊列空,等待數據"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.poll(); // 每次移走隊首元素 queue.notify(); System.out.println("從隊列取走一個元素,隊列剩余" + queue.size() + "個元素"); } } } } class Producer extends Thread { @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == size) { try { System.out.println("隊列滿,等待有空余空間"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.offer(1); // 每次插入一個元素 queue.notify(); System.out.println("向隊列取中插入一個元素,隊列剩余空間:" + (size - queue.size())); } } } } }
這段代碼在很多講述生產者-消費者模式的地方都會用到,其中,Producer線程首先啟動,synchronized關鍵字使其能夠獲得queue的鎖,其他線程處於等待狀態。初始queue為空,通過offer向緩沖區隊列寫入數據,notify()方法使得等待該緩沖區queue的線程(此處為消費者線程)喚醒,但該線程並不能馬上獲得queue的鎖,只有等生產者線程不斷向queue中寫入數據直到queue.size() == size,此時緩沖隊列充滿,生產者線程調用wait()方法進入等待狀態。此時,消費者線程處於喚醒並且獲得queue的鎖,通過poll()方法消費緩沖區中的數據,同理,雖然調用了notify()方法使得生產者線程被喚醒,但其並不能馬上獲得queue的鎖,只有等消費者線程不斷消費數據直到queue.size() == 0,消費者線程調用wait()方法進入等待狀態,生產者線程重新獲得queue的鎖,循環上述過程,從而完成生產者線程與消費者線程的協作。
在Android的SystemServer中有多處用到了線程協作的方式,比如WindowManagerService的main()中通過runWithScissors()啟動的BlockingRunnable與SystemServer所在線程的協作。WindowManagerService源碼地址可參考:https://github.com/android/platform_frameworks_base/blob/master/services/core/java/com/android/server/wm/WindowManagerService.java
在Java中“中斷”線程是通過interrupt()方法來實現的,之所以加引號,是因為interrupt()並不中斷正在運行的線程,只是向線程發送一個中斷請求,具體行為依賴於線程的狀態,在文檔中有如下說明:
Posts an interrupt request to this Thread. The behavior depends on the state of this Thread:
翻譯下:
public void interrupt() { // Interrupt this thread before running actions so that other // threads that observe the interrupt as a result of an action // will see that this thread is in the interrupted state. VMThread vmt = this.vmThread; if (vmt != null) { vmt.interrupt(); } synchronized (interruptActions) { for (int i = interruptActions.size() - 1; i >= 0; i--) { interruptActions.get(i).run(); } } }
join()方法也可以理解為線程之間協作的一種方式,當兩個線程需要順序執行時,調用第一個線程的join()方法能使該線程阻塞,其依然通過wait()方法來實現的。
/** * Blocks the current Thread (<code>Thread.currentThread()</code>) until * the receiver finishes its execution and dies. * * @throws InterruptedException if <code>interrupt()</code> was called for * the receiver while it was in the <code>join()</code> call * @see Object#notifyAll * @see java.lang.ThreadDeath */ public final void join() throws InterruptedException { VMThread t = vmThread; if (t == null) { return; } synchronized (t) { while (isAlive()) { t.wait(); } } }
另外,還有帶時間參數的join()方法,在超出規定時間後,退出阻塞狀態。同樣的,其通過帶時間參數的wait()方法實現而已。
public final void join(long millis) throws InterruptedException{} public final void join(long millis, int nanos) throws InterruptedException {}
sleep()與wait()的相同之處在於它們都是通過等待阻塞線程,不同之處在於sleep()等待的是時間,wait()等待的是對象的鎖。
public static void sleep(long time) throws InterruptedException { Thread.sleep(time, 0); }
public static void sleep(long millis, int nanos) throws InterruptedException { VMThread.sleep(millis, nanos); }
CountDownLatch位於java.util.concurrent.CountDownLatch,實現倒數計數鎖存器,當計數減至0時,觸發特定的事件。在某些主線程需要等到子線程的應用很實用,以Google的zxing開源庫中的一段代碼為例進行說明:
final class DecodeThread extends Thread { …… private final CountDownLatch handlerInitLatch; DecodeThread(CaptureActivity activity, Collection<BarcodeFormat> decodeFormats, Map<DecodeHintType,?> baseHints, String characterSet, ResultPointCallback resultPointCallback) { this.activity = activity; handlerInitLatch = new CountDownLatch(1); …… } Handler getHandler() { try { handlerInitLatch.await(); } catch (InterruptedException ie) { // continue? } return handler; } @Override public void run() { Looper.prepare(); handler = new DecodeHandler(activity, hints); handlerInitLatch.countDown(); Looper.loop(); } }在上述例子中,首先在DecodeThread構造器中初始化CountDownLatch對象,並傳入初始化參數1。其次,在run()方法中調用CountDownLatch對象的countDown()方法,這很好的保證了外部實例通過getHandler()方法獲取handler時,handler不為null。
通過JAVA代碼獲取手機的一些基本信息(本機號碼,SDK版本,系統版本,手機型號),javasdk代碼如下: package com.zzw.getPhoneInfos
Building Apps with Over 65K Methods(解決APP引用方法總數超過65536),appsapp 本
Android提權漏洞CVE-2014-7920&CVE-2014-7921分析,android提權漏洞沒羽@阿裡移動安全,更多安全類技術干貨,請訪問阿裡聚安全博
Android中的 Multiple dex files define 編譯錯誤引發的思考 昨天我龍哥問我一個問題,他說如果一個工程中,有一個com.x.A枚舉,導入