设计模式、代码设计、volatile Sington单例

 

开闭原则
不改变主程序的情况下 扩展系统功能
对扩展开放,对修改封闭 建立稳定且灵活的系统

创建模式-抽象工厂
利用接口实现及对象管理措施 实现不同子工厂模式切换

结构模式中-适配器模式
利用继承抽象类、接口等方式把两个不相干的类实体组合起来使用

结构模式中-代理模式
不直接操作对象 通过代理类操作 比如一些权限控制 一些公共逻辑的统一管理

 

里氏替换原则是继承复用的基础,是对开闭原则的补充,也是对实现抽象化的具体步骤的规范
里氏替换原则:
任何父类对象出现的地方,我们都可以用其子类对象来替换,并且能保证原有程序的逻辑行为和正确性
实施方案:
子类不能重新定义父类的方法
里氏替换原则的含义:
共享父类方法,那么共享的父类方法就不能被子类重新定义,子类只能通过添加新方法来扩展功能,父类和子类都能实例化时,子类继承的方法和父类一样,在父类调用方法的地方,子类也能调用同一个继承来的、逻辑和父类一致的方法,这时用子类对象替换父类对象,逻辑一致。

依赖倒置

以发Kafka消息为例,KafkaProducer不应写在业务逻辑里,这样高层模块即业务主类就会依赖producer,
后续如果想替换为其他的mq消息,代码侵入性就极大。
用依赖倒置解决:
可以写一个发消息的接口,写一个接口实现,用kafka发消息,后续要替换消息组件,只需要改消息实现就行了

 

原子类和volatile有什么区别
都能够确保读取到的是主内存中的最新值

volatile 通过内存屏障(Memory Barrier)和禁止指令重排序来确保可见性和有序性
AtomicInteger原子类通过 CAS(Compare-And-Swap)来实现无锁的原子性操作。CAS 是硬件层面的原子操作,
它通过比较期望值和当前值,如果相等则更新为新值,否则操作失败,并通过循环重试来实现原子性

volatile 禁止了与其相关变量的指令重排序,以确保操作顺序正确
‌指令重排序(Instruction Reordering)‌是指在程序执行过程中,编译器和处理器为了提高性能,对指令的执行顺序进行重新排序的一种优化技术
内存屏障‌: 内存屏障是一种同步原语,用于限制指令重排。它可以确保在屏障之前的指令完成后,才能执行屏障之后的指令

指令重排实例:
public void mySort() {
     int x = 11; //语句1
     int y = 12; //语句2  谁先执行效果一样
     x = x + 5;  //语句3
     y = x * x;  //语句4
 }
执行顺序是:1 2 3 4、2 1 3 4、1 3 2 4
指令重排也有限制不会出现:4321,语句 4 需要依赖于 y 以及 x 的申明,因为存在数据依赖,无法首先执行

指令重排实例:
int num = 0;
 boolean ready = false;
 // 线程1 执行此方法
 public void actor1(I_Result r) {
     if(ready) {
         r.r1 = num + num;
     } else {
         r.r1 = 1;
     }
 }
 // 线程2 执行此方法
 public void actor2(I_Result r) {
     num = 2;
     ready = true;
 }
情况一:线程 1 先执行,ready = false,结果为 r.r1 = 1
情况二:线程 2 先执行 num = 2,但还没执行 ready = true,线程 1 执行,结果为 r.r1 = 1
情况三:线程 2 执行 ready = true,线程 1 执行,进入 if 分支结果为 r.r1 = 4
情况四:线程 2 先执行 ready = true,切换到线程 1,进入 if 分支为 r.r1 = 0,再切回线程 2 执行 num = 2,发生指令重排

volatile测试

如果没有volatile关键字,则不能保证这种可见性,t2会一直循环
    volatile boolean bo = true;// volatile
    @Test
    public void test2() throws InterruptedException {

        Thread t2 = new Thread(){
            @Override
            public void run(){
                while (bo){
                    //线程挂起后,也会重新获取主内存值,循环终止  请注释掉
//                    try {
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
                }
                System.out.println("exit t2 thread");
            }
        };
        t2.start();
        Thread.sleep(1000);

        Thread t1 = new Thread(){
            @Override
            public void run(){
                bo = false;
            }
        };
        t1.start();
        Thread.sleep(5000);
        System.out.println("主线程执行了");
    }

  

当声明一个final成员时,必须在构造函数退出前设置它的值
final类型的成员变量的值,包括那些用final引用指向的collections的对象,是读线程安全而无需使用synchronization的

 

1,恶汉 线程安全 效率低

2,饱汉 线程安全 效率低

 

posted @ 2020-06-14 18:48  XUMT111  阅读(178)  评论(0)    收藏  举报