天天看點

異步指派簡介

簡介

同步指派就是我們最常用的方式

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        test.setValue("123");
        System.out.println(test.getValue());
        
    }
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
           

其中test對象中的value set 指派 get取值。這個在單線程中是沒問題的。

但是假如說變成多線程呢。

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        new Thread(() -> {
            //1指派
            test.setValue("123");
        }).start();
        //2擷取值
        System.out.println(test.getValue());
    }

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

           

這種情況指派與擷取值,就沒辦法保證先後順序。擷取值的時候有可能另一個線程還沒有指派,或者,已經指派,但線程間的緩存還不一緻。

那線程間的值同步該怎麼做?

方案一 synchronized

package com.hcq.async;

/**
 * 同步方法一
 */
public class Model1 {
    //使用volatile 防止線程緩存
    private volatile String value;

    public synchronized String getValue() {
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return value;
    }

    public synchronized void setValue(String value) {
        //先指派,再同步
        this.value = value;
        this.notify();
    }
}
           

這種方案是最簡單的一步方案。

缺點:

  • 鎖比較重
  • 多次指派有些問題。
  • 如果指派線程在先,會出現死鎖問題。

方案二 synchronized+ volatile

public class Model2 {
    //使用volatile 防止線程緩存
    private volatile String value;

    private volatile boolean isOk = false;

    public synchronized String getValue() {
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return value;
    }

    public synchronized void setValue(String value) {
        if (this.isOk == true) {
            System.out.println("已經指派[" + this.value + "],指派【" + value + "]失敗!");
        } else {
            //先指派,再同步
            this.isOk = true;
            this.value = value;
            System.out.println("指派[" + this.value+"]");
            this.notify();
        }

    }
}

           

這種方案解決了多次指派問題。

  • 鎖比較重
  • 如果指派線程在先,會出現死鎖問題。

方案三 CountDownLatch + Cas

public class Model3 {
    //使用volatile 防止線程緩存
    private volatile String value;
    private AtomicBoolean isOk = new AtomicBoolean(false);

    private CountDownLatch lock = new CountDownLatch(1);

    public String getValue() {
        try {
            lock.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return value;
    }

    public void setValue(String value) {
        //幂等操作
        if (isOk.compareAndSet(false, true)) {
            //先指派,再同步
            this.value = value;
            System.out.println("指派[" + this.value+"]");
            this.lock.countDown();
        } else {
            System.out.println("已經指派[" + this.value + "],指派【" + value + "]失敗!");
        }
    }
}

           

用AtomicBoolean 來解決重複調用的幂等問題。

用CountDownLatch解決 先指派死鎖問題。

代碼: https://github.com/jlhuang9/iptest/tree/master/src/main/java/com/hcq/async