多线程案例1

1.单例模式

1.1 什么是单例模式

单例模式是设计模式的一种,设计模式就是用来规范程序员代码的一种模板,使代码可控

而单例模式就是要保证只有一个实例对象的模式

1.2 怎么实现

单例模式有两个实现的方法分别为饿汉模式和懒汉模式

1.2.1 饿汉模式

就是直接在类里定义单例时就进行了初始化操作

public class SingletonHungry {
    private static SingletonHungry singletonLazy = new SingletonHungry();
    //构造方法私有化:为了返回相同的实例
    private SingletonHungry(){}
    //提供一个公开方法返回singletonLazy对象,为了让外界获取到singletonLazy
    public static SingletonHungry getInstance(){
        return singletonLazy;
    }
}

这种写法是最简单的,但是一般在开发环境里,为了节约资源,我们有时候会想在要用的时候再创建实例,不想一开始就创建实例,为了让实例在使用时,才初始化于是就有了懒汉模式 

1.2.2 懒汉模式,面试常考,需要会用手默写

就是先定义为空,不占据资源,等要用到的时候才创建

实现中必须要加锁,因为cpu调度的随机性,可能t1还没执行完就被调度到第二个里面了,导致出现了两个不同的实例

public class SingletonLazy {
    private static SingletonLazy singletonLazy = null;
    private SingletonLazy(){}
    public static SingletonLazy getInstance() {
        if(singletonLazy == null){//防止多次使用synchronized浪费cpu资源
            synchronized (SingletonLazy.class) {
                if (singletonLazy == null) {
                    singletonLazy = new SingletonLazy();
                }
        }

        }return singletonLazy;
    }
}

 关于synchronized的位置问题:synchronized要加在哪才能彻底解决呢

我们发现出现线程安全的主要是进行了多次的初始化操作

 

2.为什么要在前面再加一个判断语句呢

加锁/解锁是⼀件开销⽐较⾼的事情.⽽懒汉模式的线程不安全只是发⽣在⾸次创建实例的时候.因此 后续使⽤的时候,不必再进⾏加锁了

所以第一个是用来判断要不要加锁,省的浪费了资源

由于共享变量instance受到了改变,所以最好还加上volatile 

2.阻塞队列

相当于在队列的基础上,只能装指定的数据,且队列满了如果继续入队列不会报错,只会等待出队列 如果队列空了,想继续出队也不会报错,只会等待入队列

如果队满了,还继续入队则后面的代码不会运行

 public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(3);
        myBlockingQueue.put(1);
        myBlockingQueue.put(2);
        myBlockingQueue.put(3);
        System.out.println("已经添加了三个元素");
        myBlockingQueue.put(4);//开始阻塞
        System.out.println("已经添加了四个元素");
        System.out.println(myBlockingQueue.get());
        System.out.println(myBlockingQueue.get());
        System.out.println(myBlockingQueue.get());
        System.out.println("已经取出了三个元素");
        System.out.println(myBlockingQueue.get());
        System.out.println("已经取出了四个元素");
    }

 

如果将添加第四个元素那行注释掉

为了更好理解,我们来初步实现阻塞队列的一些功能

 首先,写出一个基本的队列

1.定义存储数据的数组

2.定义头指针和尾指针,来入队和出队

3.定义元素数量

然后再在此基础上加入wait和notify,来实现阻塞功能

public class MyBlockingQueue {
    //定义队列需要的数组
    private Integer[] elementData;
    //定义头尾下标
    private volatile int head = 0;
    private volatile int tail = 0;
    //定义元素的个数
    private volatile int size = 0;

    public MyBlockingQueue(int copacity){//构造阻塞队列
        if(copacity <= 0){
            throw new RuntimeException("队列容量必须大于0.");
        }
        elementData = new Integer[copacity];
    }

    //插入数据的方法
    public void put(Integer value) throws InterruptedException {
        synchronized (this){
            //判断是否满了
            while(size >= elementData.length){//使用while循环是为了防止虚假等待
                this.wait();
                //阻塞队列在队列满时会进行阻塞等待
            }
            elementData[tail] = value;
            tail++;
            if(tail >= elementData.length){
                tail = 0;
            }
            size++;
            this.notifyAll();//用来通知get,告诉队列里有数据了能取了
        }

        //唤醒阻塞线程
    }

    //取出数据的方法
    public synchronized Integer get() throws InterruptedException {

            while (size == 0){
                this.wait();
            }
            Integer value = elementData[head];
            head++;
            if(head >= elementData.length){
                head = 0;
            }
            size--;
            this.notifyAll();//用来给put进行通知,告诉有空位了可以入队了
            return value;

    }
}

 

2.1生产者消费者模型

public class Demo_703 {
    public static void main(String[] args) {
        // 定义一个阻塞队列, 交易场所
        MyBlockingQueue queue = new MyBlockingQueue(100);

        // 创建生产者线程
        Thread producer = new Thread(() -> {
            int num = 0;
            // 使用循环不停的向队列中添加元素,直到队列容量占满
            while (true) {
                try {
                    // 添加元素
                    queue.put(num);
                    System.out.println("生产了元素:" + num);
                    num++;
                    // 休眠一会
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 启动
        producer.start();

        // 定义一个消费者线程
        Thread consumer = new Thread(() -> {
            // 不断的从队列中取出元素
            while (true) {
                try {
                    // 取元素
                    Integer value = queue.take();
                    System.out.println("消费了元素:" + value);
                    // 休眠一会
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 启动消费者线程
        consumer.start();
    }
}

生产者消费者模型就是利用了阻塞队列(消息队列)来在中间充当信息的部分,然后其他就是定义生产者线程和消费者线程即可

 阻塞队列就是为了防止操作者再进行很多次的wait,notify操作而产生的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值