CopyOnWritejava
樂觀鎖android
Java內存模型
Java內存模型定義了一種多線程訪問Java內存的規範。
(1)Java內存模型將內存分爲了主內存和工做內存。類的狀態,也就是類之間共享的變量,是存儲在主內存中的,每次Java線程用到這些主內存中的變量的時候,會讀一次主內存中的變量,並讓這些內存在本身的工做內存中有一份拷貝,運行本身線程代碼的時候,用到這些變量,操做的都是本身工做內存中的那一份。在線程代碼執行完畢以後,會將最新的值更新到主內存中去
(2)定義了幾個原子操做,用於操做主內存和工做內存中的變量程序員
volatile關鍵字的做用
volatile關鍵字的做用主要有兩個:
(1)使用volatile關鍵字修飾的變量,保證了其在多線程之間的可見性
原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。
有序性:即程序執行的順序按照代碼的前後順序執行。
可見性:指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值數據庫
當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。保證了每次讀取到volatile變量,必定是最新的數據。編程
(2)代碼底層執行不像咱們看到的高級語言----Java程序這麼簡單,它的執行是Java代碼-->字節碼-->根據字節碼執行對應的C/C++代碼-->C/C++代碼被編譯成彙編語言-->和硬件電路交互,現實中,爲了獲取更好的性能JVM可能會對指令進行重排序,多線程下可能會出現一些安全的問題。使用volatile則會對禁止語義重排序,固然這也必定程度上下降了代碼執行效率
從實踐角度而言,volatile的一個重要做用就是和CAS結合,保證了原子性,詳細的能夠參見java.util.concurrent.atomic包下的類,好比AtomicInteger。api
Synchronized和Volatile的比較緩存
ThreadLocal安全
進程和線程的區別?多線程有什麼好處?
進程:正在進行中的程序(直譯)。
線程:就是進程中一個負責程序執行的控制單元(執行路徑) 服務器
線程池的好處網絡
線程的sleep()方法和yield()方法、wait有什麼區別?
wait 和 sleep 區別?
sleep來自Thread類,和wait來自Object類
調用sleep()方法的過程當中,線程不會釋放對象鎖。而 調用 wait 方法線程會釋放對象鎖
sleep(milliseconds)須要指定一個睡眠時間,時間一到會自動喚醒
請說出與線程同步以及線程調度相關的方法
wait:使一個線程處於等待(阻塞/凍結)狀態,而且釋放所持有的對象的鎖;
sleep:使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
notify():喚醒一個處於等待狀態的線程,固然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程,並且與優先級無關;
notityAll():喚醒全部處於等待狀態的線程,該方法並非將對象的鎖給全部線程,而是讓它們競爭,只有得到鎖的線程才能進入就緒狀態;
全喚醒都判斷效率不高,能不能只喚醒對方呢?
jdk1.5之後將同步和鎖封裝成了對象。並將操做鎖的隱式方式定義到了該對象中,將隱式動做變成了顯示動做。
爲何stop()方法被廢棄而不被使用呢?緣由是stop()方法太過於暴力,會強行把執行一半的線程終止。這樣會就不會保證線程的資源正確釋放,一般是沒有給與線程完成資源釋放工做的機會,所以會致使程序工做在不肯定的狀態下。
使用boolean類型的變量,來終止線程
public static class ChangeObjectThread extends Thread { // 用於中止線程 private boolean stopMe = true; public void stopMe() { stopMe = false; } @Override public void run() { while (stopMe) { synchronized (ThreadStopSafeBoolean.class) { int v = (int) (System.currentTimeMillis() / 1000); user.setId(v); // to do sth try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } user.setName(String.valueOf(v)); } // 讓出CPU,給其餘線程執行 Thread.yield(); } } }
synchronized關鍵字的用法?
當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?
不能,一個對象的一個synchronized方法只能由一個線程訪問。
線程安全問題產生的緣由:
當一個線程在執行操做共享數據的多條代碼過程當中,其餘線程參與了運算,就會致使線程安全問題的產生。
解決思路
就是將多條操做共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,其餘線程時不能夠參與運算。必需要當前線程把這些代碼都執行完畢後,其餘線程才能夠參與運算。
使用鎖機制:synchronized 或 lock 對象
同步的好處:解決了線程的安全問題。
同步的弊端:相對下降了效率,由於同步外的線程的都會判斷同步鎖。
同步的前提:同步中必須有多個線程並使用同一個鎖。
同步函數和同步代碼塊的區別:
同步函數的鎖是固定的this。同步代碼塊的鎖是任意的對象。建議使用同步代碼塊。
簡述synchronized 和Lock的異同?
Condition接口
替代了Object中的wait notify notifyAll方法。
將這些監視器方法單獨進行了封裝,變成Condition監視器對象,能夠任意鎖進行組合。
編寫多線程程序有幾種實現方式?
一種是繼承Thread類;另外一種是實現Runnable接口。兩種方式都要經過重寫run()方法來定義線程的行爲,推薦使用後者,由於Java中的繼承是單繼承,一個類有一個父類,若是繼承了Thread類就沒法再繼承其餘類了,顯然使用Runnable接口更爲靈活。Runnable不是線程,是線程裏運行的代碼
class Demo implements Runnable// extends Fu //準備擴展Demo類的功能,讓其中的內容能夠做爲線程的任務執行 { public void run() { show(); } public void show() { for (int x = 0; x < 20; x++) { System.out.println(Thread.currentThread().getName() + "....." + x); } } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t2.start(); } }
舉例說明同步和異步。
啓動一個線程是調用run()仍是start()方法?
啓動一個線程是調用start()方法,使線程所表明的虛擬處理機處於可運行狀態,這意味着它能夠由JVM 調度並執行,這並不意味着線程就會當即運行。run()方法是線程啓動後要進行回調(callback)的方法。
線程的基本狀態以及狀態之間的關係?
建立並運行線程:
運行狀態(Running):Java運行系統經過調度選中一個處於就緒狀態的線程,使其佔有CPU並轉爲運行狀態。此時,系統真正執行線程的run()方法。
能夠經過Thread類的isAlive方法來判斷線程是否處於就緒/運行狀態:當線程處於就緒/運行狀態時,isAlive返回true,當isAlive返回false時,可能線程處於阻塞狀態,也可能處於中止狀態。
a) 當執行了某個線程對象的sleep()等阻塞類型的方法時,該線程對象會被置入一個阻塞集內,等待超時而自動甦醒。
b) 當多個線程試圖進入某個同步區域時,沒能進入該同步區域的線程會被置入鎖定集,直到得到該同步區域的鎖,進入就緒狀態。
c) 當線程執行了某個對象的wait()方法時,線程會被置入該對象的等待集中,知道執行了該對象的notify()方法wait()/notify()方法的執行要求線程首先得到該對象的鎖。
屢次start一個線程會怎麼樣
會拋出java.lang.IllegalThreadStateException 線程狀態非法異常,一個線程是不能執行兩次的,一但start()以後,結束了就不能再去調用,像你的T1同樣,結束就沒了,除非再new Thread(),要執行一樣的代碼,能夠一直產生新的線程去調用
如何在兩個線程之間共享數據
經過在線程之間共享對象就能夠了,而後經過wait(讓線程處於凍結狀態,被wait的線程會被存儲到線程池中 )/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是爲線程之間共享數據而設計的
爲何wait()方法和notify()/notifyAll()方法要在同步塊中被調用
這是JDK強制的,wait()方法和notify()/notifyAll()方法在調用前都必須先得到對象的鎖
wait()方法和notify()/notifyAll()方法在放棄對象監視器時有什麼區別
wait()方法和notify()/notifyAll()方法在放棄對象監視器的時候的區別在於:wait()方法當即釋放對象監視器(鎖),notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢纔會放棄對象監視器。
一個線程若是出現了運行時異常會怎麼樣
若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個某個對象的監視器,那麼這個對象監視器會被當即釋放
同步方法和同步塊,哪一個是更好的選擇
同步塊,這意味着同步塊以外的代碼是異步執行的,這比同步整個方法更提高代碼的效率。請知道一條原則:同步的範圍越小越好。
死鎖:
指兩個或兩個以上的線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象。就是多個線程同時被阻塞,它們中的一個或者所有都在等待某個資源被釋放。
public class DeadlockTest { public static void main(String[] args) { String str1 = new String("資源1"); String str2 = new String("資源2"); new Thread(new Lock(str1, str2), "線程1").start(); new Thread(new Lock(str2, str1), "線程2").start(); } } class Lock implements Runnable { private String str1; private String str2; public Lock(String str1, String str2) { super(); this.str1 = str1; this.str2 = str2; } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "運行"); synchronized (str1) { System.out.println(Thread.currentThread().getName() + "鎖住" + str1); Thread.sleep(1000); synchronized (str2) { // 執行不到這裏 System.out.println(Thread.currentThread().getName() + "鎖住" + str2); } } } catch (Exception e) { e.printStackTrace(); } } } 線程1運行 線程1鎖住資源1 線程2運行 線程2鎖住資源2
線程1運行線程1鎖住資源1線程2運行線程2鎖住資源2兩個線程是同時執行的,線程1鎖住了資源1,線程2鎖住了資源2,線程1企圖鎖住資源2,可是資源2已經被線程2鎖住了,線程2企圖鎖住資源1,可是資源1已經被線程1鎖住了,而後就死鎖了。
你的同步(鎖)有個人同步,個人同步有你同步
要出現死鎖問題須要知足如下條件
解決方法
線程池相關方法
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {...}
線程池的參數
自定義線程池ThreadPoolExecutor
自定義ThreadManager類管理多線程,維護三類線程池,例如請求網絡數據線程交由長時間任務線程池執行,訪問數據庫交由短期任務線程池執行,圖片下載任務將由單任務線程池執行
public class ThreadManager { private ThreadManager() { } private static ThreadManager instance = new ThreadManager(); private ThreadPoolProxy longPool; private ThreadPoolProxy shortPool; public static ThreadManager getInstance() { return instance; } // 聯網比較耗時 // 開啓線程數通常是cpu的核數*2+1 public synchronized ThreadPoolProxy createLongPool() { if (longPool == null) { longPool = new ThreadPoolProxy(5, 5, 5000L); } return longPool; } // 操做本地文件 public synchronized ThreadPoolProxy createShortPool() { if(shortPool==null){ shortPool = new ThreadPoolProxy(3, 3, 5000L); } return shortPool; } public class ThreadPoolProxy { private ThreadPoolExecutor pool; private int corePoolSize; private int maximumPoolSize; private long time; public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long time) { this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.time = time; } /** * 執行任務 * @param runnable */ public void execute(Runnable runnable) { if (pool == null) { pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, time, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10)); } pool.execute(runnable); // 調用線程池 執行異步任務 } /** * 取消任務 * @param runnable */ public void cancel(Runnable runnable) { if (pool != null && !pool.isShutdown() && !pool.isTerminated()) { pool.remove(runnable); // 取消異步任務 } } } }
多個線程在處理同一資源,可是任務卻不一樣。
生產者和消費者在同一時間段內共用同一個存儲空間,生產者向空間裏存放數據,而消費者取用數據,若是不加以協調可能會出現如下狀況:
存儲空間已滿,而生產者佔用着它,消費者等着生產者讓出空間從而去除產品,生產者等着消費者消費產品,從而向空間中添加產品。互相等待,從而發生死鎖。
wait()和notify()方法的實現
緩衝區滿和爲空時都調用wait()方法等待,當生產者生產了一個產品或者消費者消費了一個產品以後會喚醒全部線程。
public class Test1 { private static Integer count = 0; private static final Integer FULL = 10; private static String LOCK = "lock"; public static void main(String[] args) { Test1 test1 = new Test1(); new Thread(test1.new Producer()).start(); new Thread(test1.new Consumer()).start(); new Thread(test1.new Producer()).start(); new Thread(test1.new Consumer()).start(); new Thread(test1.new Producer()).start(); new Thread(test1.new Consumer()).start(); } class Producer implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } synchronized (LOCK) { while (count == FULL) { try { LOCK.wait(); } catch (Exception e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "生產者生產,目前總共有" + count); LOCK.notifyAll(); } } } } class Consumer implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (LOCK) { while (count == 0) { try { LOCK.wait(); } catch (Exception e) { } } count--; System.out.println(Thread.currentThread().getName() + "消費者消費,目前總共有" + count); LOCK.notifyAll(); } } } } }
Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它經過協調各個線程,以保證合理的使用公共資源,在操做系統中是一個很是重要的問題,能夠用來解決哲學家就餐問題。
newFixedThreadPool
建立一個固定線程數量的線程池,示例爲:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 1; i <= 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { String threadName = Thread.currentThread().getName(); Log.v("zxy", "線程:"+threadName+",正在執行第" + index + "個任務"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
newSingleThreadExecutor
建立一個只有一個線程的線程池,每次只能執行一個線程任務,多餘的任務會保存到一個任務隊列中,等待線程處理完再依次處理任務隊列中的任務,示例爲:
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); for (int i = 1; i <= 10; i++) { final int index = i; singleThreadPool.execute(new Runnable() { @Override public void run() { String threadName = Thread.currentThread().getName(); Log.v("zxy", "線程:"+threadName+",正在執行第" + index + "個任務"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
newCachedThreadPool
建立一個能夠根據實際狀況調整線程池中線程的數量的線程池,示例爲:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 1; i <= 10; i++) { final int index = i; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { String threadName = Thread.currentThread().getName(); Log.v("zxy", "線程:" + threadName + ",正在執行第" + index + "個任務"); try { long time = index * 500; Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
爲了體現該線程池能夠自動根據實現狀況進行線程的重用,而不是一味的建立新的線程去處理任務,設置了每隔1s去提交一個新任務,這個新任務執行的時間也是動態變化的。
newScheduledThreadPool
建立一個能夠定時或者週期性執行任務的線程池,示例爲:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); //延遲2秒後執行該任務 scheduledThreadPool.schedule(new Runnable() { @Override public void run() { } }, 2, TimeUnit.SECONDS); //延遲1秒後,每隔2秒執行一次該任務 scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { } }, 1, 2, TimeUnit.SECONDS);
newSingleThreadScheduledExecutor
建立一個能夠定時或者週期性執行任務的線程池,該線程池的線程數爲1,示例爲:
ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor(); //延遲1秒後,每隔2秒執行一次該任務 singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { String threadName = Thread.currentThread().getName(); Log.v("zxy", "線程:" + threadName + ",正在執行"); } },1,2,TimeUnit.SECONDS);