多线程(七):单例模式+阻塞式队列

多线程(七):单例模式+阻塞式队列

单例模式

单例模式:整个程序的运行中只存储了一个对象。

饿汉模式

优点:不用加锁也是线程安全的。

饿汉模式的实现,示例代码如下:

/*
 * 饿汉模式
 */

public class ThreadDemo84 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接创建)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)
        private static Singleton singleton = new Singleton();

        //3.提供公共的获取实例的方法
        public static Singleton getInstance() {
            return singleton;
        }
    }
    
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
    }
}

该代码的执行结果如下:
在这里插入图片描述
单例模式的饿汉模式成功得到创建。

缺点:程序启动之后就会创建,但是创建完了之后有可能不会使用,从而浪费了系统资源。

所以我们需要另一种单例模式——懒汉模式。

懒汉模式

当程序启动之后并不会进行初始化;而是在什么时候调用,什么时候再初始化。

版本1,示例代码如下:

/*
 * 懒汉模式 v1
 */

public class ThreadDemo85 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接实例化)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)
        private static Singleton singleton = null;

        //3.提供公共的获取实例的方法
        public static Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    public static void main(String[] args) {
        //创建第一个对象
        Singleton s1 = Singleton.getInstance();
        //创建第二个对象
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

该代码的执行结果如下:
在这里插入图片描述
我们发现,两个对象使用的是同一个私有变量。但是,此代码存在问题:会造成线程不安全,示例代码如下:

/*
 * 懒汉模式 v1
 */

public class ThreadDemo86 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接实例化)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)
        private static Singleton singleton = null;

        //3.提供公共的获取实例的方法
        public static Singleton getInstance() {
            if (singleton == null) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    private static Singleton s1 = null;
    private static Singleton s2 = null;

    public static void main(String[] args) throws InterruptedException {
        //创建新线程执行任务
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                s1 = Singleton.getInstance();
            }
        });
        t1.start();

        //使用主线程执行任务
        s2 = Singleton.getInstance();
        //等待t1执行完毕。
        t1.join();
        System.out.println(s1 == s2);
    }
}

该代码的执行结果如下:
在这里插入图片描述
可见,此程序是线程不安全的。

对程序进行优化,加锁可以解决线程非安全的问题,懒汉模式版本2,示例代码如下:

/*
 * 懒汉模式 v2
 */
 
public class ThreadDemo87 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接实例化)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)
        private static Singleton singleton = null;

        //3.提供公共的获取实例的方法
        public static synchronized Singleton getInstance() {
            if (singleton == null) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    private static Singleton s1 = null;
    private static Singleton s2 = null;

    public static void main(String[] args) throws InterruptedException {
        //创建新线程执行任务
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                s1 = Singleton.getInstance();
            }
        });
        t1.start();

        //使用主线程执行任务
        s2 = Singleton.getInstance();
        //等待t1执行完毕。
        t1.join();
        System.out.println(s1 == s2);
    }
}

该代码的执行结果如下:
在这里插入图片描述
我们发现:该程序的线程不安全问题得到解决,但是,此程序会带来新的问题:无论是否为第一次访问,都需要排队执行,造成性能不佳。
我们对此程序再进行优化(使用双重校验锁< Double Check >),懒汉模式版本3,示例代码如下:

/*
 * 懒汉模式 v3
 */

public class ThreadDemo88 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接实例化)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)
        private static Singleton singleton = null;

        //3.提供公共的获取实例的方法
        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

    private static Singleton s1 = null;
    private static Singleton s2 = null;

    public static void main(String[] args) throws InterruptedException {
        //创建新线程执行任务
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                s1 = Singleton.getInstance();
            }
        });
        t1.start();

        //使用主线程执行任务
        s2 = Singleton.getInstance();
        //等待t1执行完毕。
        t1.join();
        System.out.println(s1 == s2);
    }
}

该程序的运行结果如下:
在这里插入图片描述
此代码当然还是有问题的,问题在于singleton = new Singleton();此行代码编译器会进行指令重排序,因此我们需要进一步优化,我们可以得到懒汉模式的最终版本

/*
 * 懒汉模式 v final
 */

public class ThreadDemo88 {
    static class Singleton {
        //1.创建私用的构造函数(防止其他类直接实例化)
        private Singleton() {
        }

        //2.定义私有变量(线程安全)(volatile)
        private static volatile Singleton singleton = null;

        //3.提供公共的获取实例的方法
        public static  Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

    private static Singleton s1 = null;
    private static Singleton s2 = null;

    public static void main(String[] args) throws InterruptedException {
        //创建新线程执行任务
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                s1 = Singleton.getInstance();
            }
        });
        t1.start();

        //使用主线程执行任务
        s2 = Singleton.getInstance();
        //等待t1执行完毕。
        t1.join();
        System.out.println(s1 == s2);
    }
}

自定义阻塞队列

生产者消费者模型:
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

在这里插入图片描述
示例代码如下:

public class ThreadDemo89 {
    static class MyBlockingQueue {
        private int[] values;//实际存储数据的数组
        private int first;//队首
        private int last;//队尾
        private int size;//队列元素的实际大小

        public MyBlockingQueue(int initial) {
            //初始化变量
            values = new int[initial];
            first = 0;
            last = 0;
            size = 0;
        }

        //添加元素(队尾)
        public void offer(int val) {
            synchronized (this) {
                //判断边界值
                if (size == values.length) {
                    //队列已满,阻塞等待
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //添加元素到队尾
                values[last++] = val;
                size++;
                //判断当前值是否为最后一个元素
                if (last == values.length) {
                    last = 0;
                }
                //尝试唤醒消费者
                this.notify();
            }
        }

        //查询方法
        public int poll() {
            int result = -1;
            synchronized (this) {
                //判断边界值
                if (size == 0) {
                    //队列为空,阻塞等待
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //获取元素
                result = values[first++];
                size--;
                //判断是否为最后一个元素
                if (first == values.length) {
                    first = 0;
                }
                //尝试唤醒生产者
                this.notify();
            }
            return result;
        }
    }

    public static void main(String[] args) {

        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(100);

        //创建生产者
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //每隔500ms,生产一个数据
                while (true) {
                    int num = new Random().nextInt(10);
                    System.out.println("生产了随机数:" + num);
                    myBlockingQueue.offer(num);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t1.start();

        //创建消费者
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    int result = myBlockingQueue.poll();
                    System.out.println("消费了数据:" + result);
                }
            }
        });
        t2.start();
    }
}

该代码的执行结果如下:
在这里插入图片描述
生产者生产一个数据,消费者就消费了此数据。
可见,我们实现了自定义阻塞队列。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值