線程和進程的本質:由CPU進行調度的併發式執行任務,多個任務被快速輪換執行,使得宏觀上具備多個線程或者進程同時執行的效果。html
線程獨立擁有本身的線程ID,堆棧,程序計數器,局部變量,寄存器組值,優先級,信號屏蔽碼,錯誤返回碼等等,線程是獨立運行的,其執行是搶佔式的。線程共享進程資源,線程之間的通訊要進程之間的通訊來得容易得多。此外,線程的建立和銷燬的開銷也遠遠小於進程的系統開銷。java
public class FirstThread extends Thread{ private int i; @Override public void run() { for(i=0;i<10;i++) System.out.println(getName()); // 繼承自Thread } public static void main(String[] args) { new FirstThread().start(); new FirstThread().start(); // 注意啓動線程須要用Start } }
public class SecondThread implements Runnable{ private int i; @Override public void run() { for(;i<10;i++) { System.out.println(Thread.currentThread().getName() + " "+ i); } } public static void main(String[] args) { SecondThread targetRunnable = new SecondThread(); new Thread(targetRunnable,"線程1").start(); new Thread(targetRunnable).start(); } }
public class ThridThread { public static void main(String[] args) { // lambda 表達式 + functionInterface 類型轉換 // Callbable: 有返回值 FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{ int i =0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName() + " "+ i); } return i; }); new Thread(task,"有返回值的線程").start(); try { System.out.println("子線程的返回值"+task.get()); }catch(Exception e) { e.printStackTrace(); } } }
注意:搶佔式策略系統:系統會給每一個執行的線程一個小的時間段來處理任務,當該時間段用完以後,系統會剝奪該線程所佔用的資源,讓其餘線程得到執行的機會。在系統調度時,還會考慮到線程的優先級問題。android
join()-線程:讓一個線程等待另外一個線程,當在某個線程執行流中調用其餘線程的join()方法,該線程將被阻塞,知道join線程執行完畢爲止。緩存
public class SleepThread { public static void main(String[] args) throws Exception{ // 注意異常 for(int i =0;i<5;i++) { System.out.println("當前時間"+new Date()); Thread.sleep(1000); } } }
yield():線程讓步,也是Thread的靜態方法,使得正在執行的線程暫停,但不會阻塞線程,只是交出CPU的控制權,將線程轉爲就緒狀態,讓系統調度器從新調度一次。當某個線程調用yield方法暫停後,只有優先級與當前線程相同,或者優先級比當前線程更高的線程纔有可能得到執行機會。安全
改變線程優先級:setPriority(int newPriority),高優先級的線程能得到更多的執行機會。網絡
線程的同步的意義在於線程安全,也就是說有多個線程併發訪問同一個對象,而線程調度的不肯定性可能帶來潛在的安全問題。多線程
同步監視器:java多線程引入同步監視器來解決同步問題,任什麼時候刻只能有一個線程得到對同步監視器的鎖定,當同步代碼塊執行完成後,該線程會釋放對同步監視器的鎖定。java容許任何對象做爲同步監視器,一般咱們使用可能被併發訪問的共享資源做爲同步監視器。併發
public class DrawThread extends Thread { private Account account; private double drawaccout; public DrawThread(String name,Account account,double drawaccount) { super(name); this.account = account; this.drawaccout= drawaccount; } public void run() { synchronized(account) { if(account.getBlance()>=drawaccount) { System.out.println(getName()+"取錢成功"); try { Thread.sleep(1); }catch(InterruptedException e) { e.printStackTrace(); } } } } }
public synchronized void draw(double amount) { …… }
class X{ private final ReentrantLock lock = new ReentrantLock(); //須要定義線程安全的方法 public void foo() { lock.lock();//加鎖 try { // 須要保證線程安全的代碼 } finally { lock.unlock();//使用finally塊保證釋放鎖 } } }
死鎖的問題:
產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
(3) 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
(4) 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。app
在系統中已經出現死鎖後,應該及時檢測到死鎖的發生,並採起適當的措施來解除死鎖。目前處理死鎖的方法可歸結爲如下四種:
1) 預防死鎖:這是一種較簡單和直觀的事先預防的方法。方法是經過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或者幾個,來預防發生死鎖。預防死鎖是一種較易實現的方法,已被普遍使用。可是因爲所施加的限制條件每每太嚴格,可能會致使系統資源利用率和系統吞吐量下降。jvm
2) 避免死鎖:該方法一樣是屬於事先預防的策略,但它並不須事先採起各類限制措施去破壞產生死鎖的的四個必要條件,而是在資源的動態分配過程當中,用某種方法去防止系統進入不安全狀態,從而避免發生死鎖。
3) 檢測死鎖:這種方法並不須事先採起任何限制性措施,也沒必要檢查系統是否已經進入不安全區,此方法容許系統在運行過程當中發生死鎖。但可經過系統所設置的檢測機構,及時地檢測出死鎖的發生,並精確地肯定與死鎖有關的進程和資源,而後採起適當措施,從系統中將已發生的死鎖清除掉。
4) 解除死鎖:這是與檢測死鎖相配套的一種措施。當檢測到系統中已發生死鎖時,須將進程從死鎖狀態中解脫出來。經常使用的實施方法是撤銷或掛起一些進程,以便回收一些資源,再將這些資源分配給已處於阻塞狀態的進程,使之轉爲就緒狀態,以繼續運行。死鎖的檢測和解除措施,有可能使系統得到較好的資源利用率和吞吐量,但在實現上難度也最大。
java中應該避免死鎖的出現。
線程安全是以犧牲程序運行效率爲代價的,所以在注意線程安全的同時,也要注意不要濫用鎖和同步方法,儘可能只對那些會改變競爭資源的方法進行同步。同時還要根據單線程和多線程運行環境來提供線程不安全和線程安全兩種版本,JDK提供的StringBuilder,StringBuffer就是一個例子。
private final Lock lock = new ReentrantLock(); // Condition實例綁定在一個Lock對象上 private final Condition cond = lock.newCondition(); public void Draw(double drawamount) { lock.lock(); try { if(!flag) cond.await();//致使當前線程等待 else { // ... cond.signalAll();// 喚醒其餘線程 } }catch(InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }
public class Testjava{ public static void main(String[] args) throws Exception{ ExecutorService pool = Executors.newFixedThreadPool(6); Runnable target = ()->{ for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName() + "的i值爲:"+ i); } }; // 向線程池中提交兩個線程 pool.submit(target); pool.submit(target); pool.shutdown(); } }
【Java8源碼分析】線程池-Executor與ExecutorService的全面剖析
class Accout{ private ThreadLocal<String> name = new ThreadLocal<>(); public Accout(String str) { this.name.set(str); } public String getname() { return name.get(); } public void setname(String str) { this.name.set(str); } }
注意:ThreadLocal與其餘同步機制都是爲了解決訪問同一資源衝突問題而出現的,可是側重的領域不一樣,同步機制爲實現多個線程對相同資源訪問的併發安全性,ThreadLocal則是隔離多個線程之間的數據共享,從而避免競爭。
Android中的多線程本質上也是Java的多線程,同時添加了一些不一樣的特性和使用的場景。其中,最主要的一個區別就是Android中主線程和子線程中的區分,Android中的主線程是UI線程,負責運行四大組件並與用戶實現交互,須要保持較高的反應速度,因此主線程不容許進行耗時的操做(好比說網絡請求和訪問),不然容易出現ANR現象,子線程則負責處理 一些耗時的任務,而若是子線程中想要實現對UI的操做,則須要經過Android的handler消息機制。
爲何子線程中不容許對UI進行操做呢
由於Android的UI控件並非線程安全,多線程的併發訪問會帶來UI控件的不可預期的狀態,且考慮到加鎖機制會帶來性能上的問題,所以Android在設計初期就禁止子線程處理UI。UI操做時ViewRootImpl會對操做者所在的線程進行checkThread,若是非主線程,會拋出CalledFromWrongThreadException。
那麼Android除了java原生的Thread/Runnable等線程形態,還有哪些包裝過了的有特色的線程形式?
很棒的參考:你真的瞭解AsyncTask
AsyncTask 是一個輕量級的一部任務類,在線程池中執行後臺任務。而後將任務的進度和最終結果傳遞給主線程,並在主線程中更新UI。
// 三個泛型參數 不須要傳遞參數時,能夠用void代替 public abstract class AsyncTask<Params,Progress,Result>
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { //在線程池中執行 該方法必須返回計算結果給onPostExecute() protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); // 可使用該方法返回任務的進度,該方法會調用onProgressUpdate() publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } // 被主線程調用執行 所以這裏能夠有UI操做 protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } // 在主線程中調用執行 任務執行結束後,會調用該方法,所以這裏能夠有UI操做 protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } // 主線程調用execute方法,執行任務前,會調用[1] onPreExecute()完成一些準備工做 // onPreExecute()是在主線程中執行 new DownloadFilesTask().execute(url1, url2, url3); // 也能夠調用cancel來取消任務的執行
onPreExecute()
,onPostExecute(Result)
,doInBackground(Params...)
, onProgressUpdate(Progress...)
public abstract class AsyncTask<Params, Progress, Result> { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU數 private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; 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); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); } // 核心線程數 = CPU數+1 // 最大線程數 = CPU數*2 + 1 // 非核心線程的超時時間爲1秒 // 任務隊列的容量爲128
Android 開發手冊寫明:AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the
java.util.concurrent
package such asExecutor
,ThreadPoolExecutor
andFutureTask
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized(this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid =-1; }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
- corePoolSize: 線程池的核心線程數,默認狀況下, 核心線程會在線程池中一直存活, 即便處於閒置狀態. 但若是將allowCoreThreadTimeOut設置爲true的話, 那麼核心線程也會有超時機制, 在keepAliveTime設置的時間事後, 核心線程也會被終止.
- maximumPoolSize: 最大的線程數, 包括核心線程, 也包括非核心線程, 在線程數達到這個值後,新來的任務將會被阻塞.
- keepAliveTime: 超時的時間, 閒置的非核心線程超過這個時長,講會被銷燬回收, 當allowCoreThreadTimeOut爲true時,這個值也做用於核心線程.
- unit:超時時間的時間單位.
- workQueue:線程池的任務隊列, 經過execute方法提交的runnable對象會存儲在這個隊列中.
- threadFactory: 線程工廠, 爲線程池提供建立新線程的功能.
- handler: 任務沒法執行時,回調handler的rejectedExecution方法來通知調用者.
若是線程池中線程的數目少於corePoolSize,就算線程池中有其餘的沒事作的核心線程,線程池仍是會從新建立一個核心線程;直到核心線程數目到達corePoolSize(常駐線程就位)
若是工做隊列滿了,而且線程池中線程的數目到達了最大數目maximumPoolSize,那麼就會用最後一個構造參數handler處理;**默認的處理方式是直接丟掉任務,而後拋出一個異常。