不可变对象设计模式

本文探讨了线程安全的概念及其实现方式,包括使用synchronized关键字和设计不可变对象。通过实例展示了如何设计一个不可变的int类型累加器,并对比了线程安全与非线程安全的累加器在多线程环境下的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程安全主要目的是在受控的并发访问中防止数据发生变化。除了使用synchronized关键字同步对资源的写操作之外,还可以在线程之间不共享资源状态,甚至将资源的状态设置为不可变。

java.lang.String的每一个方法都没有同步修饰,但是在多线程访问的情况下是线程安全的,Java8中通过Stream修饰的ArrayList在函数式方法并行访问的情况下也是线程安全的。

所谓“不可变对象”是没有机会去修改它,每一次的修改都将产生一个新的对象,String str ="hello";str = str+"world"这会产生新的字符串。

非线程安全的累加器:

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * 非线程安全的累计器
 */
public class IntegerAccumulator {
    private  int init;

    //构造时传入初始值
    public IntegerAccumulator(int init){
        this.init = init;
    }

    public int add(int i){
        this.init += i;
        return this.init;
    }

    public int getValue(){
        return this.init;
    }


    public  static void main(String args[]){
        IntegerAccumulator integerAccumulator = new IntegerAccumulator(0);
        IntStream.range(0,3).forEach(i -> new Thread(()->{
            int inc = 0;
            while(true){             
                int oldValue = integerAccumulator.getValue();
                int result = integerAccumulator.add(inc);
                System.out.println(oldValue  + "+" + inc + "=" + result);
                if(inc + oldValue != result){
                    System.out.println("ERROR " + oldValue + "+" + inc + "=" +result);
                }
                inc++;
                slowly();
            }
        }).start());
    }

    private static void slowly(){
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

运行结果: 

方法同步增加线程安全性:

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * 方法同步增加线程安全
 * 
 */
public class IntegerAccumulator {
    private  int init;

    //构造时传入初始值
    public IntegerAccumulator(int init){
        this.init = init;
    }

    public int add(int i){
        this.init += i;
        return this.init;
    }

    public int getValue(){
        return this.init;
    }


    public  static void main(String args[]){
        IntegerAccumulator integerAccumulator = new IntegerAccumulator(0);
        IntStream.range(0,3).forEach(i -> new Thread(()->{
            int inc = 0;
            while(true){
                int oldValue;
                int result;
                synchronized (IntegerAccumulator.class){

                     oldValue = integerAccumulator.getValue();
                     result = integerAccumulator.add(inc);
                }
                System.out.println(oldValue  + "+" + inc + "=" + result);
                if(inc + oldValue != result){
                    System.out.println("ERROR" + oldValue + "+" + inc + "=" +result);
                }
                inc++;
                slowly();
            }
        }).start());
    }

    private static void slowly(){
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

运行结果:

...

 

设计一个类似java.lang.String的不可变的int类型累加器

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * 不可变的累计器设计
 * 不可变对象不允许被继承
 * final修饰防止由于继承引起的线程安全问题
 */
public final class IntegerAccumulator {
    
    //final修饰不允许线程对其改变,在构造函数中赋值后不会再改变
    private final  int init;

    //构造时传入初始值
    public IntegerAccumulator(int init){
        this.init = init;
    }

//    public int add(int i){
//        this.init += i;
//        return this.init;
//    }

    //构造新的累加器,需要用到另外一个accumulator和初始值
    public IntegerAccumulator(IntegerAccumulator integerAccumulator,int init){
        this.init = integerAccumulator.getValue() + init;
    }

    //每次相加都会产生一个新的IntegerAccumulator,不会在原有的init基础上增加,不修改原有的IntegerAccumulator
    public IntegerAccumulator add(int i){
        return new IntegerAccumulator(this,i);
    }

    public int getValue(){
        return this.init;
    }


    public  static void main(String args[]){
        IntegerAccumulator integerAccumulator = new IntegerAccumulator(0);
        IntStream.range(0,3).forEach(i -> new Thread(()->{
            int inc = 0;
            while(true){
                int oldValue = integerAccumulator.getValue();
                int result = integerAccumulator.add(inc).getValue();
                System.out.println(oldValue  + "+" + inc + "=" + result);
                if(inc + oldValue != result){
                    System.out.println("ERROR " + oldValue + "+" + inc + "=" +result);
                }
                inc++;
                slowly();
            }
        }).start());
    }

    private static void slowly(){
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

运行结果:不会出现ERROR情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值