進(jìn)程:進(jìn)程就是正在執(zhí)行的程序,。 線程:是程序執(zhí)行的一條路徑, 一個(gè)進(jìn)程中可以包含多條線程,。 通俗理解:例如你打開微信就是打開一個(gè)進(jìn)程,在微信里面和好友視頻聊天就是開啟了一條線程,。 兩者之間的關(guān)系: 一個(gè)進(jìn)程里面可以有多條線程,,至少有一條線程。 一條線程一定會(huì)在一個(gè)進(jìn)程里面,。
關(guān)于進(jìn)程與線程的講解,,這篇文章講的挺好的-->進(jìn)程與線程的一個(gè)簡單解釋 http://www./blog/2013/04/processes_and_threads.html 一、繼承Thread1、定義一個(gè)類MyThread繼承Thread,,并重寫run方法,。 2、將要執(zhí)行的代碼寫在run方法中,。 3,、創(chuàng)建該類的實(shí)例,并調(diào)用start()方法開啟線程,。 代碼如下:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //3,、創(chuàng)建該類的實(shí)例,并調(diào)用start()方法開啟線程,。 MyThread myThread = new MyThread(); myThread.start(); } //1,、定義一個(gè)類MyThread繼承Thread,并重寫run方法,。 class MyThread extends Thread { public void run() { //2、將執(zhí)行的代碼寫在run方法中,。 for (int i = 0; i < 100; i++) { Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + " i=" + i); } } } }
二,、實(shí)現(xiàn)Runnable接口1、定義一個(gè)類MyRunnable實(shí)現(xiàn)Runnable接口,,并重寫run方法,。 2、將要執(zhí)行的代碼寫在run方法中,。 3,、創(chuàng)建Thread對(duì)象, 傳入MyRunnable的實(shí)例,并調(diào)用start()方法開啟線程,。 代碼如下:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //3,、創(chuàng)建Thread對(duì)象, 傳入MyRunnable的實(shí)例,并調(diào)用start()方法開啟線程,。 Thread thread = new Thread(new MyRunnable()); thread.start(); } //1,、定義一個(gè)類MyRunnable實(shí)現(xiàn)Runnable接口,并重寫run方法,。 class MyRunnable implements Runnable { public void run() { //2,、將執(zhí)行的代碼寫在run方法中。 for (int i = 0; i < 100; i++) { Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + " i=" + i); } } } }
三,、實(shí)現(xiàn) Callable 接口Callable 是類似于 Runnable 的接口,,實(shí)現(xiàn) Callable 接口的類和實(shí)現(xiàn) Runnable 的類都是可被其它線程執(zhí)行的任務(wù)。 1,、自定義一個(gè)類 MyCallable 實(shí)現(xiàn) Callable 接口,,并重寫call()方法 2、將要執(zhí)行的代碼寫在call()方法中 3,、創(chuàng)建線程池對(duì)象,,調(diào)用submit()方法執(zhí)行MyCallable任務(wù),,并返回Future對(duì)象 4、調(diào)用Future對(duì)象的get()方法獲取call()方法執(zhí)行完后的值 代碼如下:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //3,、創(chuàng)建線程池對(duì)象,,調(diào)用submit()方法執(zhí)行MyCallable任務(wù),并返回Future對(duì)象 ExecutorService pool = Executors.newSingleThreadExecutor(); Future<Integer> f1 = pool.submit(new MyCallable()); //4,、調(diào)用Future對(duì)象的get()方法獲取call()方法執(zhí)行完后的值 try { Log.d(TAG, "sum=" + f1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //關(guān)閉線程池 pool.shutdown(); } //1,、自定義一個(gè)類MyCallable實(shí)現(xiàn)Callable接口,并重寫call()方法 public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { //2,、將要執(zhí)行的代碼寫在call()方法中 int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } return sum; } } }
一,、繼承 Thread 類與實(shí)現(xiàn) Runnable 接口的區(qū)別我們都知道 java 支持單繼承,多實(shí)現(xiàn),。實(shí)現(xiàn) Runnable 接口還可以繼承其他類,,而使用繼承 Thread 就不能繼承其他類了。所以當(dāng)你想創(chuàng)建一個(gè)線程又希望繼承其他類的時(shí)候就該選擇實(shí)現(xiàn) Runnable 接口的方式,。 二,、實(shí)現(xiàn) Callable 接口與 Runnable 接口的區(qū)別Callable 執(zhí)行的方法是 call() ,而 Runnable 執(zhí)行的方法是 run(),。 call() 方法有返回值還能拋出異常,, run() 方法則沒有沒有返回值,也不能拋出異常,。 一,、概念一個(gè)進(jìn)程中開啟了不止一個(gè)線程。 二,、多線程的優(yōu)缺點(diǎn)優(yōu)點(diǎn) 1,、提高CPU的使用率 例如朋友圈發(fā)表圖片,當(dāng)你上傳9張圖片的時(shí)候,,如果開啟一個(gè)線程用同步的方式一張張上傳圖片,,假設(shè)每次上傳圖片的線程只占用了CPU 1%d的資源,剩下的99%資源就浪費(fèi)了,。但是如果你開啟9個(gè)線程同時(shí)上傳圖片,,CPU就可以使用9%的資源了。 2,、提高程序的工作效率 還是拿朋友圈發(fā)表圖片來說,,假設(shè)開啟一個(gè)線程上傳一張圖片的時(shí)間是1秒,那么同步的方式上傳9張就需要9秒,,但是你開啟9個(gè)線程同時(shí)上傳圖片,,那么就只需要1秒就完成了。 缺點(diǎn) 1、如果有大量的線程,會(huì)影響性能,因?yàn)镃PU需要在它們之間切換,。 2,、更多的線程需要更多的內(nèi)存空間。 3,、多線程操作可能會(huì)出現(xiàn)線程安全或者死鎖等問題,。
三、多線程并行和并發(fā)的區(qū)別概念 并行:多個(gè)處理器或者多核處理器同時(shí)執(zhí)行多個(gè)不同的任務(wù),。 并發(fā):一個(gè)處理器處理多個(gè)任務(wù),。 打個(gè)比喻 并行就是一個(gè)人用他的左手喂女兒吃飯,同時(shí)用右手喂兒子吃飯,。 并發(fā)就是一個(gè)人先喂女兒吃一口飯,,接著喂兒子吃一口,然后再喂女兒吃一口,,輪流喂,。 舉個(gè)多線程并發(fā)操作同一數(shù)據(jù)出現(xiàn)線程安全的例子 利用多線程上傳9張圖片,并提示還剩幾張圖片未上傳,。代碼如下:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //開啟2條線程上傳圖片 MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable, "線程1").start(); new Thread(myRunnable, "線程2").start(); } public class MyRunnable implements Runnable { private int imgNum = 9;//圖片數(shù)量 @Override public void run() { while (true) { if (imgNum == 0) { break; } try { Thread.sleep(1000);//模擬上傳一張圖片需要1秒鐘的時(shí)間 } catch (InterruptedException e) { e.printStackTrace(); } Log.d(TAG, Thread.currentThread().getName() + "正在上傳圖片...,,還剩" + imgNum-- + "張圖片未上傳"); } } } }
打印結(jié)果如下: 由圖可知,圖片數(shù)量出現(xiàn)了負(fù)數(shù),,顯然是錯(cuò)誤的,。
原因: 出現(xiàn)這種錯(cuò)誤的原因是有多個(gè)線程在操作共享的數(shù)據(jù),。即一個(gè)線程在操作共享數(shù)據(jù)的過程中CPU切換到其他線程又對(duì)該數(shù)據(jù)進(jìn)行操作,,這就是所謂的多線程并發(fā)。 解決: 把操作數(shù)據(jù)的那段代碼用 synchronized 進(jìn)行同步, 這樣就能保證在同一時(shí)刻只能有一個(gè)線程能夠訪問,。 代碼如下:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //開啟2條線程上傳圖片 MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable, "線程1").start(); new Thread(myRunnable, "線程2").start(); } public class MyRunnable implements Runnable { private int imgNum = 9;//圖片數(shù)量 @Override public void run() { while (true) { synchronized (MyRunnable.class) { //加上synchronized進(jìn)行同步,,保證在同一時(shí)刻只能有一個(gè)線程能夠訪問。 if (imgNum == 0) { break; } try { Thread.sleep(1000);//模擬上傳一張圖片需要1秒鐘的時(shí)間 } catch (InterruptedException e) { e.printStackTrace(); } Log.d(TAG, Thread.currentThread().getName() + "正在上傳圖片...,,還剩" + imgNum-- + "張圖片未上傳"); } } } } }
打印結(jié)果如下: 關(guān)于線程池前面舉的朋友圈發(fā)表圖片的多線程例子中,,為了提高CPU的使用率和程序的工作效率就需要?jiǎng)?chuàng)建9個(gè)線程來上傳圖片。但是線程的創(chuàng)建和銷毀是非常耗CPU和內(nèi)存的,,因?yàn)樗婕暗揭c操作系統(tǒng)進(jìn)行交互,。這樣就可能導(dǎo)致創(chuàng)建與銷毀線程的開銷比實(shí)際業(yè)務(wù)還大,而線程池就能很好的解決這些問題,。線程池里的每一個(gè)線程結(jié)束后,,并不會(huì)銷毀(可以設(shè)置超時(shí)銷毀),而是回到線程池中成為空閑狀態(tài),,等待下一個(gè)對(duì)象來使用,。 使用線程池的優(yōu)點(diǎn)1、減少創(chuàng)建與銷毀線程帶來的性能開銷。 2,、可控制最大并發(fā)線程數(shù),,避免過多資源競爭而導(dǎo)致系統(tǒng)內(nèi)存消耗完。 3,、能更好的控制線程的開啟與回收,,并且能定時(shí)執(zhí)行任務(wù)。 線程池中重要的幾個(gè)類Executor:java中線程池的頂級(jí)接口,,可以稱它為一個(gè)執(zhí)行器,,通過查看源碼也知道,他只有一個(gè)簡單的方法execute(Runnable command),,就是用來執(zhí)行提交的任務(wù),。源碼如下: 【Executor.java】
public interface Executor { void execute(Runnable command); }
ExecutorService:Executor的子類,也是真正的線程池接口,。它提供了提交任務(wù)和關(guān)閉線程池等方法,。調(diào)用submit方法提交任務(wù)還可以返回一個(gè)Future對(duì)象,利用該對(duì)象可以了解任務(wù)執(zhí)行情況,,獲得任務(wù)的執(zhí)行結(jié)果或取消任務(wù),。 Executors:由于線程池配置比較復(fù)雜,自己配置的線程池可能性能不是最好的,。Executors就是用來方便創(chuàng)建各種常用線程池的工具類,。 ThreadPoolExecutor:ExecutorService的默認(rèn)實(shí)現(xiàn),Executors創(chuàng)建各種線程池的時(shí)候內(nèi)部其實(shí)就是調(diào)用了ThreadPoolExecutor的構(gòu)造方法,。下面通過查看源碼驗(yàn)證,。 例如隨便創(chuàng)建一個(gè)線程池:
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
點(diǎn)擊newCachedThreadPool()進(jìn)去,里面確實(shí)調(diào)用了ThreadPoolExecutor的構(gòu)造方法,,如下: 【Executor.java】 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
ThreadPoolExecutor構(gòu)造函數(shù)參數(shù)說明下面從源碼中拿一個(gè)參數(shù)最完整的來講解,,如下: 【ThreadPoolExecutor.java】 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }
corePoolSize:核心線程數(shù),如果運(yùn)行的線程數(shù)少于corePoolSize,,當(dāng)有新的任務(wù)過來時(shí)會(huì)創(chuàng)建新的線程來執(zhí)行這個(gè)任務(wù),,即使線程池中有其他空閑的線程。 maximumPoolSize:線程池中允許的最大線程數(shù),。 keepAliveTime:如果線程數(shù)多于核心線程數(shù),,那么這些多出來的線程如果空閑時(shí)間超過keepAliveTime將會(huì)被終止。 unit:keepAliveTime參數(shù)的時(shí)間單位,。 workQueue:任務(wù)隊(duì)列,,通過線程池的execute方法會(huì)將任務(wù)Runnable存儲(chǔ)在隊(duì)列中。 threadFactory:線程工廠,,用來創(chuàng)建新線程,。 handler:添加任務(wù)出錯(cuò)時(shí)的策略捕獲器,,默認(rèn)是ThreadPoolExecutor.AbortPolicy ,即添加任務(wù)出錯(cuò)就直接拋出異常 ,。
四種線程池newFixedThreadPool:創(chuàng)建固定大小的線程池,,這樣可以控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待,。如果線程池中的某個(gè)線程由于異常而結(jié)束,,線程池則會(huì)再補(bǔ)充一條新線程。 例子:創(chuàng)建線程數(shù)為3的線程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int finalI = i; newFixedThreadPool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000); Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + " i=" + finalI); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
打印結(jié)果: 由打印結(jié)果可知,,10個(gè)任務(wù)始終在3個(gè)線程中執(zhí)行,。
newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池,即這個(gè)線程池永遠(yuǎn)只有一個(gè)線程在運(yùn)行,,這樣能保證所有任務(wù)按指定順序來執(zhí)行,。如果這個(gè)線程異常結(jié)束,那么會(huì)有一個(gè)新的線程來替代它,。 例子:
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int finalI = i; newSingleThreadExecutor.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000); Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + " i=" + finalI); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
打印結(jié)果:
由打印結(jié)果可知,,10個(gè)任務(wù)始終在3個(gè)線程中執(zhí)行。
newCachedThreadPool:創(chuàng)建帶有緩存的線程池,,在執(zhí)行新的任務(wù)時(shí),,當(dāng)線程池中有之前創(chuàng)建的可用線程就重用之前的線程,否則就新建一條線程,。如果線程池中的線程在60秒未被使用,,就會(huì)將它從線程池中移除。 例子:
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int finalI = i; newCachedThreadPool.execute(new Runnable() { @Override public void run() { Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + " i=" + finalI); } }); }
由打印結(jié)果可知,,線程1出現(xiàn)了很多次,,說明有重用之前創(chuàng)建的線程。
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3); /** * 延遲2秒執(zhí)行任務(wù) */ newScheduledThreadPool.schedule(new Runnable() { @Override public void run() { Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + "定時(shí)任務(wù)"); } }, 2, TimeUnit.SECONDS); /** * 延遲1秒后每2秒執(zhí)行一次任務(wù) */ newScheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { Log.d(TAG, "線程名字:" + Thread.currentThread().getName() + "周期性任務(wù)"); } }, 1, 2, TimeUnit.SECONDS);
由打印結(jié)果可知,,定時(shí)任務(wù)是 2 秒后執(zhí)行任務(wù),,周期性任務(wù)是延遲 1 秒后每 2 秒執(zhí)行一次任務(wù),。 優(yōu)秀人才不缺工作機(jī)會(huì),只缺適合自己的好機(jī)會(huì),。但是他們往往沒有精力從海量機(jī)會(huì)中找到最適合的那個(gè),。100offer 會(huì)對(duì)平臺(tái)上的人才和企業(yè)進(jìn)行嚴(yán)格篩選,讓「最好的人才」和「最好的公司」相遇,。掃描下方二維碼,,注冊(cè) 100offer,談?wù)勀銓?duì)下一份工作的期待,。一周內(nèi),,收到 5-10 個(gè)滿足你要求的好機(jī)會(huì),!
|