多線程之volative關鍵字

輕量級同步機制:volative關鍵字

volative的做用

關鍵做用是使變量在多個線程之間可見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("結束");
        }
    }
}

image-20210310223438566

程序並無由於我修改以後結束運行,由於線程對共享變量具備不可見性,main線程修改布爾值以後,子線程看不到值的修改。所以要想實現線程的可見性這裏能夠加上volative關鍵字修飾公共變量算法

volative關鍵字的做用:使線程在強制公共內存中讀取變量值,保證可見性數組

volatile非原子特性

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();
        }
    }
}

image-20210310232145063

按道理輸出1000的整數倍數纔對,可是變量在自增的過程當中沒有更新到又被讀取再修改,所以volatile不具有原子性,正確辦法將方法加上synchronized關鍵字安全

image-20210310232349272

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();
        }

    }
}

image-20210310234739741

CAS

CAS(Compare And Swap)是由硬件實現的,

CAS能夠將read(讀)-modify(修改)-write(寫)轉化爲原子操做

i++自增過程:

從主內存調取i變量值

對i值加1

再把加1事後的值保存到主內存

CAS原理:在把數據更新到主內存時,再次讀取主內存變量的值,若是如今變量的值與指望的值同樣就更新。

image-20210311220552964

使用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;
    }
}

image-20210311223403577

CAS中的ABA問題

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

使用AtomicLong定義計數器

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());
    }
}

image-20210315213254758

相關文章
相關標籤/搜索