原创

12.Java设计模式问题


目录介绍

  • 12.0.0.1 实现单例模式有几种方法?懒汉式中双层锁的目的是什么?两次判空的目的又是什么?
  • 12.0.0.2 如何理解生产者消费者模型?写一下生产者消费者模型的代码案例?解决关键问题的思路主要是什么?
  • 12.0.0.3 看下面代码,利用面向对象设计原则如何改进,可以保证代码后期的拓展性和解耦问题?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

12.0.0.1 实现单例模式有几种方法?懒汉式中双层锁的目的是什么?两次判空的目的又是什么?

  • 懒汉式:延迟加载,同时也要保证多线程环境下会产生多个single对象
    public class Singleton { 
        private Singleton() {} 
        private volatile static Singleton instance;
        //第一层锁:保证变量可见性 
        public static Singleton getInstance() { 
            if (single == null) {//第一次判空:无需每次都加锁,提高性能 
                synchronized (Singleton.class) {//第二层锁:保证线程同步
                    if (single == null) {//第二次判空:避免多线程同时执行getInstance()产生多个single对象 
                        single = new Singleton(); 
                    } 
                } 
            } 
            return single; 
        } 
    }
    
  • 饿汉式:在类加载初始化时就创建好一个静态的对象供外部使用
    public class Singleton { 
        private Singleton() {} 
        private static Singleton single = new Singleton(); 
        public static Singleton getInstance() { 
            return single; 
        } 
    }
    

12.0.0.3 如何理解生产者消费者模型?写一下生产者消费者模型的代码案例?

  • 生产者消费者模型发生场景
    • 多线程-并发协作(生产者消费者模型)。多线程同步的经典问题!
  • 什么是生产者消费者模型,举例式说明
    • 准确说应该是“生产者-消费者-仓储”模型
    • 1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
    • 2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
    • 3、当消费者发现仓储没产品可消费时候会通知生产者生产。
    • 4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
  • 专业术语说明什么是生产者消费者模型
    • 生产者消费者模型通过一个缓存队列,既解决了生产者和消费者之间强耦合的问题,又平衡了生产者和消费者的处理能力。
    • 具体规则:生产者只在缓存区未满时进行生产,缓存区满时生产者进程被阻塞;消费者只在缓存区非空时进行消费,缓存区为空时消费者进程被阻塞;当消费者发现缓存区为空时会通知生产者生产;当生产者发现缓存区满时会通知消费者消费。
    • 实现关键:synchronized保证对象只能被一个线程占用;wait()让当前线程进入等待状态,并释放它所持有的锁;notify()&notifyAll()唤醒一个(所有)正处于等待状态的线程
  • 遇到关键问题
    • 如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。
    • 如何保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区空时消耗数据?
  • 实际开发时案例
    • 这种并发情况下,一般服务端程序用的比较多,Android端的应用程序较少有什么并发情况。虽然事实如此,但是构建生产者-消费者模型,是线程间协作的思想,工作线程的协助是为了让UI线程更好的完成工作,提高用户体验。比如,图片选择查看器案例!
    • 简单案例:https://www.cnblogs.com/linjiqin/p/3217050.html
  • 解决关键问题
    • 如何保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区空时消耗数据,思考一下?
    • 解决思路可以简单概括为:
      • 生产者持续生产,直到缓冲区满,满时阻塞;缓冲区不满后,继续生产;
      • 消费者持续消费,直到缓冲区空,空时阻塞;缓冲区不空后,继续消费;
      • 生产者和消费者都可以有多个;
    • 能够让消费者和生产者在各自满足条件需要阻塞时能够起到正确的作用
      • wait()/notify()方式;
      • await()/signal()方式;
      • BlockingQueue阻塞队列方式;
      • PipedInputStream/PipedOutputStream方式;
    • 一般可以使用第一种和第三种方式实现逻辑。

12.0.0.3 看下面代码,利用面向对象设计原则如何改进,可以保证代码后期的拓展性和解耦问题?

  • 将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展?
  • 看看下面这段代码,改编某伟大公司产品代码,你觉得可以利用面向对象设计原则如何改进?
    public class VIPCenter {
        void serviceVIP(T extend User user>) {
         if (user instanceof SlumDogVIP) {
            // 穷 X VIP,活动抢的那种
            // do somthing
          } else if(user instanceof RealVIP) {
            // do somthing
          }
          // ...
    }
    
    • 这段代码的一个问题是,业务逻辑集中在一起,当出现新的用户类型时,比如,大数据发现了我们是肥羊,需要去收获一下, 这就需要直接去修改服务方法代码实现,这可能会意外影响不相关的某个用户类型逻辑。
    • 利用开关原则,可以尝试改造为下面的代码。将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展。技术博客大总结
    public class VIPCenter {
        private Map<User.TYPE, ServiceProvider> providers;
        void serviceVIP(T extend User user) {
            providers.get(user.getType()).service(user);
        }
    }
    
    interface ServiceProvider{
        void service(T extend User user) ;
    }
    
    class SlumDogVIPServiceProvider implements ServiceProvider{
        void service(T extend User user){
            // do somthing
        }
    }
    
    class RealVIPServiceProvider implements ServiceProvider{
        void service(T extend User user) {
            // do something
        }
    }
    

评论

留言