Concurrency 处理共享可变性的函数式编程的替代方案是什么?

Concurrency 处理共享可变性的函数式编程的替代方案是什么?,concurrency,functional-programming,immutability,Concurrency,Functional Programming,Immutability,在网上看了一些视频后,我越来越感兴趣地研究我的基于降低共享可变状态复杂性的编码决策。函数式编程/Lambda演算似乎是克服共享可变状态问题的最流行标准。但还有其他选择吗?函数式编程是解决这个问题的一种合理的默认方法,现在有没有一种共识 免责声明: 我知道这篇文章可能不会直接回答你的问题。 然而,许多程序员仍然忽视了它们有时可以避免共享的可变性。我想在这里用一个例子和希望向你们展示,它对你们有帮助 TL;DR:问问自己,非共享可变性还是共享不变性也可以选择。 如果怀疑您是否真的需要共享可变性呢?

在网上看了一些视频后,我越来越感兴趣地研究我的基于降低共享可变状态复杂性的编码决策。函数式编程/Lambda演算似乎是克服共享可变状态问题的最流行标准。但还有其他选择吗?函数式编程是解决这个问题的一种合理的默认方法,现在有没有一种共识

免责声明:
我知道这篇文章可能不会直接回答你的问题。 然而,许多程序员仍然忽视了它们有时可以避免共享的可变性。我想在这里用一个例子和希望向你们展示,它对你们有帮助

TL;DR:问问自己,非共享可变性还是共享不变性也可以选择。

如果怀疑您是否真的需要共享可变性呢?
如果将两个术语中的一个转换为相反的术语,则可以得到两个有用的备选方案:

  • 非共享可变性
  • 共享不变性
让我们以Java8为例来说明我的意思。 此共享可变性示例使用同步来避免可见性问题和竞争条件:

public class MutablePoint {
    private int x, y;

    void move(int dx, int dy) {
        x += dx;
        y += dy;
    }

    @Override
    public String toString() {
        return "MutablePoint{x=" + x + ", y=" + y + '}';
    }
}
   
public class SharedMutability {
    public static void main(String[] args) {
        final MutablePoint mutablePoint = new MutablePoint();
        final Thread moveRightThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (mutablePoint) {
                    mutablePoint.move(1, 0);
                }
                Thread.yield();
            }
        }, "moveRight");
        final Thread moveDownThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (mutablePoint) {
                    mutablePoint.move(0, 1);
                }
                Thread.yield();
            }
        }, "moveDown");

        final Thread displayThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (mutablePoint) {
                    System.out.println(mutablePoint);
                }
                Thread.yield();
            }
        }, "display");

        moveRightThread.start();
        moveDownThread.start();
        displayThread.start();
    }
}
公共类可变点{
私有整数x,y;
无效移动(整数dx,整数dy){
x+=dx;
y+=dy;
}
@凌驾
公共字符串toString(){
返回“可变点{x=“+x+”,y=“+y+'}”;
}
}
公共类共享可交换性{
公共静态void main(字符串[]args){
最终可变点可变点=新可变点();
最终线程moveRightThread=新线程(()->{
对于(int i=0;i<1000;i++){
已同步(可变点){
可变点移动(1,0);
}
螺纹屈服强度();
}
}“移动权利”);
最终线程移动下线程=新线程(()->{
对于(int i=0;i<1000;i++){
已同步(可变点){
可变点移动(0,1);
}
螺纹屈服强度();
}
},“向下移动”);
最终线程显示线程=新线程(()->{
对于(int i=0;i<1000;i++){
已同步(可变点){
System.out.println(可变点);
}
螺纹屈服强度();
}
}“显示器”);
moveRightThread.start();
moveDownThread.start();
displayThread.start();
}
}
说明:
我们有3条线。当两个线程moveRight和moveDown在可变点上写入时,单线程显示从可变点读取。所有3个线程必须在可变点上同步,以避免可见性问题和争用条件

如何应用非共享可变性? 非共享意味着“只有一个线程在可变对象上读写”。 你不需要太多。这很容易。始终只能从同一个线程访问一个可变对象。因此,您不需要关键字synchronize、任何锁或关键字volatile。此外,如果这一线程只专注于读取和写入可变对象中的值,那么它可以在没有锁和内存障碍的情况下非常快速。 但是,您仅限于这一个线程。这通常是没有问题的,除非你用I/O之类的任务来阻止这一个线程(不要这样做!)。此外,您必须确保可变对象不会因为被分配到一个线程之外的变量或字段并从那里访问而以某种方式“逃逸”

如果将非共享可变性应用于该示例,它可能如下所示:

public class UnsharedMutability {
    private static final ExecutorService accessorService = Executors.newSingleThreadExecutor(); // only ONE thread!
    private static final MutablePoint mutablePoint = new MutablePoint();

    public static void main(String[] args) {
        final Thread moveRightThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    mutablePoint.move(1, 0);
                });
                Thread.yield();
            }
        }, "moveRight");
        final Thread moveDownThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    mutablePoint.move(0, 1);
                });
                Thread.yield();
            }
        }, "moveDown");
        final Thread displayThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    System.out.println(mutablePoint);
                });
                Thread.yield();
            }
        }, "display");

        moveRightThread.start();
        moveDownThread.start();
        displayThread.start();
    }
}
public class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    ImmutablePoint move(int dx, int dy) {
        return new ImmutablePoint(x+dx, y+dy);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ImmutablePoint that = (ImmutablePoint) o;
        return x == that.x && y == that.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "ImmutablePoint{x=" + x + ", y=" + y + '}';
    }
}

public class SharedImmutability {
    private static AtomicReference<ImmutablePoint> pointReference = new AtomicReference<>(new ImmutablePoint(0, 0));

    public static void main(String[] args) {
        final Thread moveRightThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                pointReference.updateAndGet(point -> point.move(1, 0));
                Thread.yield();
            }
        }, "moveRight");
        final Thread moveDownThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                pointReference.updateAndGet(point -> point.move(0, 1));
                Thread.yield();
            }
        }, "moveDown");
        final Thread displayThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(pointReference.get());
                Thread.yield();
            }
        }, "display");

        moveRightThread.start();
        moveDownThread.start();
        displayThread.start();
    }
}
公共类非共享可交换性{
private static final ExecutorService accessorService=Executors.newSingleThreadExecutor();//只有一个线程!
私有静态final MutablePoint MutablePoint=新的MutablePoint();
公共静态void main(字符串[]args){
最终线程moveRightThread=新线程(()->{
对于(int i=0;i<1000;i++){
accessorService.submit(()->{
可变点移动(1,0);
});
螺纹屈服强度();
}
}“移动权利”);
最终线程移动下线程=新线程(()->{
对于(int i=0;i<1000;i++){
accessorService.submit(()->{
可变点移动(0,1);
});
螺纹屈服强度();
}
},“向下移动”);
最终线程显示线程=新线程(()->{
对于(int i=0;i<1000;i++){
accessorService.submit(()->{
System.out.println(可变点);
});
螺纹屈服强度();
}
}“显示器”);
moveRightThread.start();
moveDownThread.start();
displayThread.start();
}
}
说明:
我们又得到了全部3条线。但是,所有3个线程不需要在可变点上同步,因为它们只访问在单线程ExecutorService accessorService中运行的同一个线程中的可变点

如何应用共享不变性? 不变性意味着“无法在对象创建后更改其状态”。不可变对象始终只有一个状态。因此,它们始终是线程安全的。不可变对象可以在需要更改时创建新的不可变对象。 但是,创建太多的对象太快可能会导致高内存消耗并导致更高的GC活动。有时,如果存在多个重复的不可变对象,则可以对其进行重复数据消除

如果将共享不变性应用于该示例,它可能如下所示:

public class UnsharedMutability {
    private static final ExecutorService accessorService = Executors.newSingleThreadExecutor(); // only ONE thread!
    private static final MutablePoint mutablePoint = new MutablePoint();

    public static void main(String[] args) {
        final Thread moveRightThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    mutablePoint.move(1, 0);
                });
                Thread.yield();
            }
        }, "moveRight");
        final Thread moveDownThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    mutablePoint.move(0, 1);
                });
                Thread.yield();
            }
        }, "moveDown");
        final Thread displayThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                accessorService.submit(() -> {
                    System.out.println(mutablePoint);
                });
                Thread.yield();
            }
        }, "display");

        moveRightThread.start();
        moveDownThread.start();
        displayThread.start();
    }
}
public class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    ImmutablePoint move(int dx, int dy) {
        return new ImmutablePoint(x+dx, y+dy);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ImmutablePoint that = (ImmutablePoint) o;
        return x == that.x && y == that.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "ImmutablePoint{x=" + x + ", y=" + y + '}';
    }
}

public class SharedImmutability {
    private static AtomicReference<ImmutablePoint> pointReference = new AtomicReference<>(new ImmutablePoint(0, 0));

    public static void main(String[] args) {
        final Thread moveRightThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                pointReference.updateAndGet(point -> point.move(1, 0));
                Thread.yield();
            }
        }, "moveRight");
        final Thread moveDownThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                pointReference.updateAndGet(point -> point.move(0, 1));
                Thread.yield();
            }
        }, "moveDown");
        final Thread displayThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(pointReference.get());
                Thread.yield();
            }
        }, "display");

        moveRightThread.start();
        moveDownThread.start();
        displayThread.start();
    }
}
公共类不可变点{
私人最终int x;
私人终审法院;
公共免疫