1.java線程特性
1.原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行 銀行轉帳,本身轉100給別人,本身帳戶少100 別人多100java
不會出現,本身少了100 別人那裏卻也沒有多100的狀況安全
2.有序性:程序執行的順序按照代碼的前後順序執行。通常來講處理器爲了提升程序運行效率,可能會對輸入代碼進行優化,它不保證程序中各個語句的執行前後順序同代碼中的順序一致,可是它會保證程序最終執行結果和代碼順序執行的結果是一致的併發
3.可見性:多個線程共同訪問同一個變量的時候,一個線程修改了該變量的值,其餘線程應該能看見這個變量的修改jvm
2.java內存模型
java內存模型也叫共享內存模型(JMM) java內存模型決定了 多個線程共享同個變量的時候,某個線程修改了共享變量,這個修改應該被其餘線程看見。ide
例如 static int a=3 當多個線程共享該變量的時候,a=3 是放在主內存中,每一個線程都有本身私有本地內存,私有本地內存存放的是共享內存變量的副本優化
線程A,B 操做主內存共享變量的時候,會先將主內存的變量複製一份到線程A,B本身的本地內存中,保存一個副本。若是要想保證線程的可見性,A 線程本地內存的副本修改之後,須要刷新到主內存之中,而後線程B在從新去主內存中刷新線程A更新之後的值。this
當前主內存的值爲0(共享變量 int a=0) 若是有兩個線程 A ,B 操做主內存的值,A,B線程會先將主內存的值 複製一份到本身的本地內存中,若是A線程修改變量atom
a的值爲1,修改的是A線程本身本地內存中的值,此時線程B 和主內存中的值仍是0,當線程A,B 通訊的時候,A線程就將本地內存中a變量的值刷新到主內存中,而後B線程spa
須要操做變量a的時候,會先到主內存中獲取最新的值,在複製到本身的本地內存中線程
3.Volatile保證變量的可見性
package com.thread; class ThreadVolatileDemo extends Thread { public boolean flag = true; @Override public void run() { System.out.println("開始執行子線程...."); while (flag) { } System.out.println("線程中止"); } public void setRuning(boolean flag) { this.flag = flag; } } public class VolatileDemo { public static void main(String[] args) throws InterruptedException { ThreadVolatileDemo threadVolatileDemo = new ThreadVolatileDemo(); threadVolatileDemo.start(); Thread.sleep(3000); threadVolatileDemo.setRuning(false); System.out.println("理想情況下設置false之後線程會中止運行"); Thread.sleep(1000); System.out.println(threadVolatileDemo.flag); } }
輸出結果:
開始執行子線程.... 理想情況下設置false之後線程會中止運行 false
(一直執行while循環,已經將flag設置成了false 可是程序一直沒有中止,是由於 該線程一直讀取的是副本,並無去刷新主內存中的值)
Volatile關鍵字將解決線程之間可見性, 強制線程每次讀取該值的時候都去「主內存」中取值
用法:public boolean volatile flag;
每一個線程操做flag的時候,都會去主內存刷新最新的flag值
4.Volatile是否能保證線程的安全性(原子性)
package com.thread; public class VolatileSyn extends Thread { public static volatile int count = 0; public void run() { for (int i = 0; i < 1000; i++) { count++; } } public static void main(String[] args) throws InterruptedException { VolatileSyn[] volatileSyns = new VolatileSyn[4]; for (int i = 0; i < volatileSyns.length; i++) { volatileSyns[i] = new VolatileSyn(); } for (VolatileSyn volatileSyn : volatileSyns) { volatileSyn.start(); } Thread.sleep(4000); System.out.println(VolatileSyn.count); } }
建立4條線程或者10條線程,共享可見變量count,每一個線程都執行+1000的操做,理論值應該是4000,可是屢次執行的結果 ,count++是線程不安全
最後的值 不等於4000(也有等於4000的) 屢次操做結果不一樣 因而可知volatile並不能保證線程的安全問題。volatile能保證線程的可見性,例如:A 線程剛從主內存刷新過來count值 正準備操做的時候,B線程 修改了count的值 此時A 的值就不是最新的了
5.AtomicInteger原子類
AtomicInteger是一個提供原子操做的Integer類,經過線程安全的方式操做加減。
package com.thread; import java.util.concurrent.atomic.AtomicInteger; public class VolatileNoAtomic extends Thread { private static AtomicInteger count = new AtomicInteger(0); @Override public void run() { for (int i = 0; i < 1000; i++) { //等同於i++ count.incrementAndGet(); //decrementAndGet } } public static void main(String[] args) throws InterruptedException { // 初始化10個線程 VolatileNoAtomic[] volatileNoAtomic = new VolatileNoAtomic[10]; for (int i = 0; i < 10; i++) { // 建立 volatileNoAtomic[i] = new VolatileNoAtomic(); } for (int i = 0; i < volatileNoAtomic.length; i++) { volatileNoAtomic[i].start(); } Thread.sleep(4000); System.out.println(VolatileNoAtomic.count); } }
6.volatile與synchronized區別
僅靠volatile不能保證線程的安全性。(原子性)
①volatile輕量級,只能修飾變量。synchronized重量級,還可修飾方法
②volatile只能保證數據的可見性,不能用來同步,由於多個線程併發訪問volatile修飾的變量不會阻塞。
synchronized不只保證可見性,並且還保證原子性,由於,只有得到了鎖的線程才能進入臨界區,從而保證臨界區中的全部語句都所有執行。多個線程爭搶synchronized鎖對象時,會出現阻塞。
線程安全性
線程安全性包括兩個方面,①可見性。②原子性。
從上面自增的例子中能夠看出:僅僅使用volatile並不能保證線程安全性。而synchronized則可實現線程的安全性。
7.wait()、notify、notifyAll()方法
wait()、notify()、notifyAll()是三個定義在Object類裏的方法,能夠用來控制線程的狀態。
這三個方法最終調用的都是jvm級的native方法。隨着jvm運行平臺的不一樣可能有些許差別。
若是對象調用了wait方法就會使持有該對象的線程把該對象的控制權交出去,而後處於等待狀態。
若是對象調用了notify方法就會通知某個正在等待這個對象的控制權的線程能夠繼續運行。
若是對象調用了notifyAll方法就會通知全部等待這個對象控制權的線程繼續運行。
注意:必定要在線程同步中使用,而且是同一個鎖的資源
8.wait和sleep的區別
1.wait是obejct方法,sleep是Thread方法
2.wait方法調用之後,須要手動喚醒,Sleep休眠 休眠時間到了之後,不須要任何操做能繼續向下執行(監控狀態依然保持)
3.wait方法放棄當前線程持有的鎖,sleep只是放棄當前cpu的持有,但當前線程仍是持有對象鎖