设计模式

作者: 磨陀货_ | 来源:发表于2019-10-14 14:54 被阅读0次

    单例

    • 单例:在一个java进程中保存一个实例

    饿汉----- 不能按需加载,可能会造成资源浪费

    /**
     * 饿汉----- 不能按需加载,可能会造成资源浪费
     */
    public class Singleton01 {
        private static final Singleton01 SINGLETON_01 = new Singleton01();
        private  Singleton01(){}
        private static Singleton01 getSingleton01(){
            return SINGLETON_01;
        }
    }
    

    懒汉----判断一下,用到的时候在创建,延时加载

    /**
     * 懒汉----延迟加载
     */
    public class Singleton02 {
        private static Singleton02 SINGLETON_01;
        private Singleton02(){}
        private static Singleton02 getSingleton01(){
            if (SINGLETON_01 == null)SINGLETON_01 = new Singleton02();
            return SINGLETON_01;
        }
    }
    

    线程问题。加锁
    synchronizaed---效率问题(这里只有null才用等待,但是synchronizaed是全等待)

    /**
     * 懒汉--- 在方法上加synchronized ,可以保证线程安全,但是效率是很大问题
     * 因为下方代码,只有为null的时候才有必要等待,但是这样任何情况都要等待,锁太多了
     */
    public class Singleton03 {
        private static Singleton03 SINGLETON_01;
        private Singleton03(){}
        private static synchronized Singleton03 getSingleton01(){
            if (SINGLETON_01 == null)SINGLETON_01 = new Singleton03();
            return SINGLETON_01;
        }
    }
    

    • 这里我们未解决上面问题,把锁的位置进行改变,但是并不完美,还是有可能出问题


      会出现new多次

    • 最后衍生出这种比较好的方式。接着往下看


      解决线程安全问题

    目前存在的问题:

    1.问题一:指令重排序(JVM会在不影响代码最终逻辑的情况下,按照自己最优的方式执行)

    当第一个线程A走到new执行时,线程B来了会直接判断不为空,直接返回

    2.问题二(JMM内存模型,引起线程可见性)

    共享变量和私有变量转换问题 多个读取,导致最后读取结果不一样
    • 解决问题:  加关键字 volatile
      1.防止指令重排序(某个变量加了volatile,就可以保证前面代码一定执行结束在执行他,他之后的代码一定是在他之后执行)
      2.保证线程可见性 (线程A 进行修改,就会强制使线程B实现【必须要再次从主存中读取】线程B改变了,线程A或者其他的都失效了)
      3.不保证原子性(下面图片)
      怎么就不保证原子性了,案例
    public class VoilatileTest {
        private static volatile  int i =0;
        private static void incr (){
            i++;
        }
        public static void test(){
            for (int j=0;j<100;j++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int k=0;k<100;k++)incr();
                    }
                }).start();
            }
        }
        public static void main(String[] args) {
            test();
            //当前存活的线程 除了主线程
            // 精灵线程(守护线程):随着守护的线程出生而出生,随着守护的线程死亡而死亡 垃圾回收
            //在正常结束程序finally是否一定执行,不一定,在守护线程中
            while (Thread.activeCount()>2){
                //礼让 让别的线程先执行
                Thread.yield();
            }
            System.out.println(i);
        }
    }
    

    运行结果是不定的,可能是10000,也可能是9998,9999,9993,9994等等。原因就是volatile不能保证原子性


    这里为什么大于2,在JVM永远存在一个守护线程,随着守护的线程出生而出生。随着守护的线程死亡而死亡(垃圾回收)

    所以就没有什么完美的解决方案,都是在项目中针对情况而定。需要哪种方式就用哪种。

    Thread.currentThread()--- 获取当前线程


    • 在正常结束程序 finally 是否一定执行?【不一定,在守护线程中】

    volatile与synchronized的区别

    • volatile可以解决可见性、防止指令重排序,但是有缺点就是不保证原子性,在高并发的情况下,会出现错误,使用时要针对情况来定是否使用
    • synchronized很强大,可以解决volatile的不保证原子性问题,但是效率很低。锁都可以解决,只是考虑效率问题,使用也是看情况定

    动态代理

    • 1.通过 jdk实现
    • 2.通过cglib实现
      spring两种都有,如果有接口就用jdk,如果没有就用cglib。也可以强制使用cglib

    /**
     * 实现了InvocationHandler 接口的类才有jdk代理功能
     */
    public class DaiLiProxy implements InvocationHandler{
        private DaiLiTest daiLiTest;
        public DaiLiProxy( DaiLiTest daiLiTest){
            this.daiLiTest = daiLiTest;
        }
        public Object getInstance(){
            //类加载器 就用被代理对象的类加载器
            //被代理对象实现接口
            //能实现代理功能的对象
            return  Proxy.newProxyInstance(daiLiTest.getClass().getClassLoader(),
                    daiLiTest.getClass().getInterfaces(),
                    this);
        }
        //method就代表你调用的方法 在我们这儿就代表show方法
        //proxy代表的就是代理对象
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("invoke方法被调用了");
            System.out.println("开启事务");
            try {
                //真正执行被代理对象的方法
                method.invoke(daiLiTest, null);
                System.out.println("提交事务");
            }catch (Exception e){
                System.out.println("回滚事务");
            }
            return null;
        }
        public static void main(String[] args) {
            //被代理对象
            DaiLiTest daiLiTest = new DaiLiTest();
            DaiLiProxy daiLiProxy = new DaiLiProxy(daiLiTest);
            //获得代理对象
            IDaiLi instance = (IDaiLi)daiLiProxy.getInstance();
            instance.show();
        }
    }
    
    public interface IDaiLi {
        void show();
    }
    
    public class DaiLiTest implements IDaiLi{
        public void show(){
            System.out.println("明天休息");
            throw new RuntimeException("出现异常");//不加这异常,得到结果图一。加上的到结果图二
        }
    }
    

    图一



    图二



    工厂模式

    反射 -- Reflect

    动态的时候可以获取他的信息,进行修改


    1.获得class对象(3种)

    实体类.class   new 实体类().getClass();   Class.forName("实体类完全限定名");

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnno {
        String value() default "";
    }
    
    @TestAnno("中午吃什么啊?")
    public class Student {
        private String name;
        public Student(String name) {this.name = name;}
        public Student(){}
        public String getName() {return name;}
        private String show(String sos,int i){
            System.out.println(name+"show一波:"+sos+"  "+i);
            return "返回值呗拿到了";
        }
    }
    
    public class ReflectTest {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
            Class<Student> studentClass = Student.class;//1.获得class对象
            Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor();//获得构造方法
            declaredConstructor.setAccessible(true);//无论是属性,构造方法,方法 只要是私有都需要设置可进入
            Student student = declaredConstructor.newInstance();//创建student对象
            System.out.println(student);//reflect.Student@1540e19d
    
            TestAnno annotation = studentClass.getAnnotation(TestAnno.class);//获得单个注解
            String value = annotation.value();
            System.out.println(value);//中午吃什么啊?
    
            //获得所有注解
            Annotation[] annotations = studentClass.getAnnotations();
            for (Annotation annotation1 : annotations) {
                System.out.println(annotation1);//@reflect.TestAnno(value=中午吃什么啊?)
            }
            //判断是否有某个注解
            boolean annotationPresent = studentClass.isAnnotationPresent(TestAnno.class);
            System.out.println(annotationPresent);//true
    
            //获得属性
            Field name = studentClass.getDeclaredField("name");
            name.setAccessible(true);
            //改值  肯定得依赖对象存在
            name.set(student,"abc");
            System.out.println(student.getName());//abc
    
            //获得方法
            Method method = studentClass.getDeclaredMethod("show", String.class, int.class);
            method.setAccessible(true);
            Object result = method.invoke(student, "跳舞", 10);
            System.out.println(result);//abcshow一波:跳舞  10            //返回值呗拿到了
        }
    }
    

    2.获得构造方法

    ``


    3.获得注解
    获得单个注解 获得所有注解 判断是否有某个注解
    4.获的属性
    获得属性

    懒汉防止不了反射攻击,懒汉final修饰,反射还是可以改值。
    饿汉就可以防止反射,饿汉加上final修饰,反射就攻击不了。


    5.获得方法

    相关文章

      网友评论

        本文标题:设计模式

        本文链接:https://www.haomeiwen.com/subject/ayifmctx.html