天天看点

异步赋值简介

简介

同步赋值就是我们最常用的方式

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