前言
上篇文章我们讲了wait和notify两个方法的使用.至此,多线程的一些基本操作就已经结束了,今天我们来谈谈多线程的一些简单应用场景.
单例模式
单例模式,顾名思义,只有一个实例的模式,我们有两种实现方式,分别是懒汉式和饿汉式,我们来分别给出代码.
饿汉式(此处的饿表示创建实例的迫切,所以我们定义成一个类变量即可,然后提供他的私有构造方法,提供获取实例的方法,由于是随着类的加载而加载,所以该实例是无线程安全问题的.)
class singleTon{ private static singleTon instance = new singleTon(); public static singleTon getInstance(){ return instance; } private singleTon(){ } }
懒汉式(需要的时候再创建,这个是在第一次使用的时候再创建实例,就有可能引发线程安全的问题,下面我给出代码)
class SingleTonLazy{ //引用指向唯一实例 private static volatile SingleTonLazy instance = null; private static final Object lock = new Object(); public static SingleTonLazy getInstance(){ if(instance == null){ synchronized (lock){ if(instance == null){ instance = new SingleTonLazy(); } } } return instance; } private SingleTonLazy(){ } }
你可能会想,这里为啥判断了两次instance不为空,并且又加了一个锁呢??
1.首先,内层if是判断是否要创建instance对象
2.加锁的原因是在多线程的情况下,可能cpu调度到线程1后执行了一个判断,就去调度到线程2了,然后再次调度到线程1时,又创建了一个实例,此时这个锁是为了帮助判空创建对象的操作保证原子性的
3.最后一个if其实是为了不让每次都持有和释放锁,只有第一次持有和释放锁,来降低锁的消耗
阻塞队列
下面我们介绍一下阻塞队列,基于这个就构成了当前经常使用的 - 消息队列
其实在日常生活中,它的使用很多,比如举例如下
淘宝等商城软件,在首页要获取到用户信息和商品信息
这样假设用户信息服务器挂了,可能导致整个服务挂了
此时我们引入消息队列
让请求接收服务器直接将请求放到信息队列中,让其他的服务器依次来取即可
好处
1.降低了服务器之间的耦合度,此时如果添加服务器可以直接添加,一个服务器宕机对其他的服务影响小
2.削峰填谷
请求多的时候,可以先存放在消息队列中慢慢处理
下面我们来简单实现一个简易消息队列(基于循环队列)
class MyBlockingQueue{ private String[] elems = null; private int head = 0; private int tail = 0; private int size = 0; private final Object lock = new Object(); public MyBlockingQueue(int capacity) { elems = new String[capacity]; } public void put(String elem) throws InterruptedException { synchronized (lock){ while (size >= elems.length){ //满了需要阻塞 lock.wait(); } //加元素 elems[tail] = elem; tail++; if(tail>=elems.length){ tail = 0; } //使用elem % elems.length也行 size++; lock.notify(); } } public String take() throws InterruptedException { synchronized (lock){ while(size == 0){ lock.wait(); //阻塞 } String elem = elems[head]; head++; if(head>= elems.length){ head = 0; } size--; lock.notify(); return elem; } } }