關鍵做用是使變量在多個線程之間可見java
public class VolativeText { public static void main(String[] args) throws InterruptedException { Student student=new Student(); new Thread(new Runnable() { @Override public void run() { student.GetMethon(); } }).start(); Thread.sleep(1000);//睡眠以後修改布爾值 student.GetName(false);//改變布爾值以結束程序運行 } static class Student { public boolean flag=true; public Student GetName(boolean flag) { this.flag=flag; return this; } public void GetMethon() { System.out.println("開始"); while (flag){//死循環 } System.out.println("結束"); } } }
程序並無由於我修改以後結束運行,由於線程對共享變量具備不可見性,main線程修改布爾值以後,子線程看不到值的修改。所以要想實現線程的可見性這裏能夠加上volative關鍵字修飾公共變量算法
volative關鍵字的做用:使線程在強制公共內存中讀取變量值,保證可見性數組
public class Text10 extends Thread { private volatile static int count; @Override public void run() { Addcount(); } public static void Addcount() { for (int i = 0; i < 1000; i++) { count++; } System.out.println(Thread.currentThread().getName()+"-->"+count); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Text10 text10=new Text10(); text10.start(); } } }
按道理輸出1000的整數倍數纔對,可是變量在自增的過程當中沒有更新到又被讀取再修改,所以volatile不具有原子性,正確辦法將方法加上synchronized關鍵字安全
volatile關鍵字是線程同步的輕量級實現,因此volatile性能確定比synchronized要好,volatile只能修飾變量,而synchronized能夠修飾方法代碼塊,在開發中使用synchronized比例仍是挺大的。多線程
多線程訪問volatile變量不會發生阻塞,而synchronized可能會阻塞。dom
volatile能保證數據的可見性,可是不能保證原子性,而synchronized能夠保證原子性,也能夠保證可見性,由於ide
synchronized會將線程的工做內存和主內存進行同步性能
volatile關鍵字保證多個線程之間的可見性,synchronized關鍵字解決線程訪問公共資源的同步性。this
區別 | synchronized | volatile |
---|---|---|
使用上 | 只能用於修飾方法、代碼塊 | 只能修飾實例變量或者類關鍵字 |
原子性保證 | 能保證,鎖能夠保護數據不被打斷 | 沒法保證 |
可見性保證 | 能保證,排它方式使同步代碼串行 | 能保證,能夠讀取公共變量 |
有序性保證 | 能保證,在同步串行的時候 | 能保證,禁止JVM以及處理器進行排序 |
阻塞狀況 | 會發生阻塞 | 不會發生阻塞 |
i++不是原子操做,除了使用synchronized進行同步,也可使用AtomicInteger/AtomicLong進行實現atom
import java.util.concurrent.atomic.AtomicInteger; public class Text10 extends Thread { private static AtomicInteger count=new AtomicInteger(); @Override public void run() { AddCount(); } public static void AddCount() { for (int i = 0; i < 1000; i++) { count.getAndIncrement();//至關於i++ //count.incrementAndGet();//至關於++i } System.out.println(Thread.currentThread().getName()+"-->"+count.get()); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Text10 text10=new Text10(); text10.start(); } } }
CAS(Compare And Swap)是由硬件實現的,
CAS能夠將read(讀)-modify(修改)-write(寫)轉化爲原子操做
i++自增過程:
從主內存調取i變量值
對i值加1
再把加1事後的值保存到主內存
CAS原理:在把數據更新到主內存時,再次讀取主內存變量的值,若是如今變量的值與指望的值同樣就更新。
public class CASText { public static void main(String[] args) { CASControl casControl=new CASControl(); for (int i = 0; i <10000 ; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"==>"+casControl.incrementAndGet()); } }).start(); } } } class CASControl { volatile static long value;//使用volatile修飾線程可見性 public long getValue() { return value; } private boolean Expect(long oldValue,long newValue) { synchronized (this) { if(value==oldValue) { value=newValue; return true; } else return false; } } public long incrementAndGet() { long oldvalue; long newValue; do { oldvalue=value; newValue=oldvalue+1; }while (!Expect(oldvalue, newValue)); return newValue; } }
CAS實現原子操做背後有一個假設:共享變量當前值與當前線程提供的指望值相同,就認爲變量沒有被其餘線程過。
實際上這種假設不必定成立,假如count=0
A線程對count值修改成10
B線程對count值修改成30
C線程對count值修改成0
當前線程看到count=0,不能認爲count沒有被其餘線程更新,這種結果是否能被接受?
這就是CAS中的ABS問題即共享變量通過了A=》B=》A的更改
是否可以接受ABA問題跟算法實現有關
若是想要規避ABA問題,能夠爲共享變量引入一個修訂號,或者時間戳,每次修改共享變量值,相應的修訂號加1.,就會變動爲[A,0]=>[B,1]=>[A,2],每次對共享變量的修改都會致使共享變量的增長,經過這個標識就能夠判斷。AtomicStampedReference類就是基於這個思想產生的。
原子類變量是基於CAS實現的,當對共享變量進行read(讀)-modify(修改)-write(寫)操做時,經過原子類變量能夠保障原子性與可見性,對變量的read(讀)-modify(修改)-write(寫)操做不是指一個簡單的賦值,而是變量的新值,依賴變量的舊值,如自增操做i++,因爲volatile只能保證原子的可見性,而不能保證原子性,原變量類內部就是一個藉助volatile變量,而且保障了該變量的read-modify-wirte操做的原子性,有時把原子變量看做一個加強的volatile變量,原子變量類有12個
分組 | 原子變量類 |
---|---|
基礎數據型 | AtomicInteger、AtomicLong、AtomicBoolean |
數組型 | AtomicIntegerArry、AtomicLongArry、AtomicReferenceArry |
字段更新器 | AtomocIntegerFiledUpdater、AtomicLongFieldUpdate、AtomicReferenceFiledUpdater |
引用型 | AtomicReference、AtomicStampedReference、AtomicMarkableReference |
import java.util.Random; import java.util.concurrent.atomic.AtomicLong; public class Text15 { //構造方法私有化 private Text15(){} //私有靜態對象 private static final Text15 text=new Text15(); //公共靜態方法返回該類的實例 public static Text15 getInstance() { return text; } //使用原子類記錄保存請求總數 成功數 失敗數 private final AtomicLong RequestCount=new AtomicLong(0); private final AtomicLong SuccessCount=new AtomicLong(0); private final AtomicLong FailCount=new AtomicLong(0); //進行自增 public void RequestCount() { RequestCount.incrementAndGet(); } public void SuccessCount() { SuccessCount.incrementAndGet(); } public void FailCount() { FailCount.incrementAndGet(); } //查看總數 public long GetRequestCount() { return RequestCount.get(); } public long GetSuccessCount() { return SuccessCount.get(); } public long GetFailCount() { return FailCount.get(); } } class Text16 { public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <1000 ; i++) { Text15.getInstance().RequestCount();//請求數量 int num=new Random().nextInt(); if(num%2==0)//若是是偶數就成功 { Text15.getInstance().SuccessCount(); } else Text15.getInstance().FailCount(); } } }).start(); Thread.sleep(1000); System.out.println("請求總數:"+Text15.getInstance().GetRequestCount()); System.out.println("請求成功"+Text15.getInstance().GetSuccessCount()); System.out.println("請求失敗"+Text15.getInstance().GetFailCount()); } }