设计模式、代码设计、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,饱汉 线程安全 效率低


浙公网安备 33010602011771号