線程和進(jìn)程
進(jìn)程和線程
操作系統(tǒng)中運(yùn)行的多個(gè)軟件丰包,一個(gè)軟件相當(dāng)于一個(gè)進(jìn)程(進(jìn)程間的數(shù)據(jù)不共享)
一個(gè)運(yùn)行中的軟件可能包含多個(gè)進(jìn)程(android:multiprocess/android:process)
一個(gè)運(yùn)行中的進(jìn)程可能包含多個(gè)線程(線程間的部分?jǐn)?shù)據(jù)可以共享)
CPU線程和操作系統(tǒng)線程
CPU線程:
多核CPU的每個(gè)核各自獨(dú)立運(yùn)行俭识,因此每個(gè)核一個(gè)線程
四核八線程:CPU硬件方在硬件級(jí)別對(duì)CPU進(jìn)行了一核多線程的支持(本質(zhì)上依然是每個(gè)核一個(gè)線程)
操作系統(tǒng)線程:
- 操作系統(tǒng)利用時(shí)間分片的方式髓霞,把CPU的運(yùn)行拆分給多條運(yùn)行邏輯,即為操作系統(tǒng)的線程
單核CPU也可以利用時(shí)間分片方式運(yùn)行多線程操作系統(tǒng)
線程是什么
按代碼順序執(zhí)行下來,執(zhí)行完畢就結(jié)束的一條線
UI線程為什么不會(huì)結(jié)束?因?yàn)樗诔跏蓟戤吅蠡趫?zhí)行死循環(huán),循環(huán)的內(nèi)容是刷線界面
多線程的使用
Thread
Thread thread = new Thread() { @Override public void run() { super.run(); //執(zhí)行Runnable的run()方法 System.out.println("Thread start"); } }; thread.start();
Runnable
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Thread with runnable start"); } }); thread.start();
ThreadFactory
ThreadFactory threadFactory = new ThreadFactory() { int count = 0; //線程安全問題 @Override public Thread newThread(Runnable r) { count++; return new Thread(r, "Thread-" + count); } }; Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " start"); } }; threadFactory.newThread(runnable).start(); threadFactory.newThread(runnable).start(); threadFactory.newThread(runnable).start();
Executor線程池
Executors.newCachedThreadPool() 常用帶緩存的線程池
Executors.newFixedThreadPool(20) 短時(shí)批量處理線程池淤井,加快處理速度
Executors.newSingleThreadExecutor() 單個(gè)線程的線程池
Executors.newSingleThreadScheduledExecutor() 具有定時(shí)功能的單線程線程池
Executors.newScheduledThreadPool(20) 具有定時(shí)功能的線程池
Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(runnable); executorService.execute(runnable); executorService.execute(runnable); executorService.shutdown();
Callable和Future
- 帶返回值的線程,獲取返回值時(shí)會(huì)阻塞當(dāng)前線程摊趾,但是可以用while輪詢?nèi)プ銎渌?/li>
Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } return "Done"; } }; ExecutorService executorService = Executors.newCachedThreadPool(); Future<String> future = executorService.submit(callable); try { String result = future.get(); System.out.println("result = " + result); } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); }
線程同步與線程安全
synchronized
- synchronized方法(該方法互斥訪問)
private synchronized void count(int newValue) { x = newValue; y = newValue; if (x != y) { System.out.println("x = " + x + ", y = " + y); } }
- synchronized代碼塊(該代碼塊互斥訪問)
private void count(int newValue) { synchronized (this){ x = newValue; y = newValue; } if (x != y) { System.out.println("x = " + x + ", y = " + y); } }
- synchronized靜態(tài)方法(該類互斥訪問)
public static synchronized void count(int newValue) { x = newValue; y = newValue; if (x != y) { System.out.println("x = " + x + ", y = " + y); } }
- synchronized 的本質(zhì)
- 保證方法內(nèi)部或代碼塊內(nèi)部的資源(數(shù)據(jù))的互斥訪問币狠。即同一時(shí)間,由同一個(gè)Monitor監(jiān)視的代碼砾层,最多只能有一個(gè)線程在訪問
- 保證線程之間對(duì)監(jiān)視資源的數(shù)據(jù)同步漩绵。即任何線程在獲取到Monitor后的第一時(shí)間,會(huì)先將共享內(nèi)存中的數(shù)據(jù)復(fù)制到自己的緩存中肛炮;任何線程在釋放Monitor的第一時(shí)間止吐,會(huì)先將緩存中的數(shù)據(jù)復(fù)制到共享內(nèi)存中
volatile
保證加了
volatile
關(guān)鍵字的字段的操作具有同步性宝踪,以及對(duì)long
和double
的操作的原子性。因此volatile可以看做簡化版的synchronizedvolatile只對(duì)基本類型(byte,char,short,int,long,float,double,boolean)的賦值操作和對(duì)象的引用賦值操作有效碍扔,但你若要修改
User.name
是不能保證同步的volatile依然解決不了
++
的原子性問題
java.util.concurrent.atomic包
- 里面有
AtomicInteger
瘩燥,AtomicBoolean
等類,作用和volatile基本一致不同,可以看做是通用版的 volatile
AtomicInteger num = new AtomicInteger(0); ...... num.getAndIncrement();
Lock & ReentrantReadWriteLock
- 同樣是加鎖機(jī)制颤芬,但使用方式更靈活,同時(shí)也更麻煩一些
Lock lock = new ReentrantLock(); ...... lock.lock(); try { x++; } finally{ //保證在方法在提前結(jié)束或出現(xiàn)Exception的時(shí)候套鹅,依然能正常釋放鎖 lock.unlock(); }
- 一般會(huì)使用更加復(fù)雜的鎖,例如
ReadWriteLock
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Lock readLock = lock.readLock(); Lock writeLock = lock.writeLock(); private int x = 0; /*** 寫操作加鎖 */ private void count(){ writeLock.lock(); try { x++; } finally{ writeLock.unlock(); } } /*** 讀操作加鎖 */ private void print (int time) { readLock.lock(); try{ for (int i = 0; i< time; i++){ System.out.print(x + " "); } System.out.println(); } finally { readLock.unlock(); } }
線程安全問題的本質(zhì)
在多個(gè)線程訪問共同的資源時(shí)汰具,在某一個(gè)線程對(duì)資源進(jìn)行寫操作的途中(寫操作已經(jīng)開始卓鹿,但是還沒有結(jié)束),其它線程對(duì)這個(gè)寫入了一半的資源進(jìn)行了讀操作留荔,或者基于這個(gè)寫了一半的資源進(jìn)行了寫操作吟孙,導(dǎo)致出現(xiàn)數(shù)據(jù)錯(cuò)誤
鎖機(jī)制的本質(zhì)
通過對(duì)共享資源進(jìn)行訪問限制,讓同一時(shí)間只有一個(gè)線程可以訪問資源聚蝶,保證了數(shù)據(jù)的準(zhǔn)確性
不論是線程安全問題杰妓,還是針對(duì)線程安全問題所衍生出的鎖機(jī)制,它們的核心都在于共享的資源碘勉,而不是某個(gè)方法或者某幾行代碼