多线程的代码案例总结(一)

代码案例

   1.单例模式:这是一种设计模式。单例模式对应的场景 有些时候希望有的对象,在整个程序中只有一个实例,只能new一次

  a)饿汉模式

  

 这个代码的执行时机 是在Singleton类被jvm加载的时候

Singleton会在JVM第一次使用的时候被加载 

 b)懒汉模式

 c)饿汉模式代码中 线程是安全的 

   d)  懒汉模式代码中线程是不安全的,

主要是在首次new对象的时候,才出现问题,一旦把对象new好之后,后续再调用getinstance 就没事了(假设有两个线程 假如两个线程都判断if里面的条件 而其中一个线程的判断会在另一个线程的判定之前,这时候instance还是null!但这时就会new出两个对象 就不是单例了

d)如何解决
   1.加锁

 

将代码加一个synchronize 

但是加锁是一个成本比较高的操作,加锁会引起程序阻塞等待

加锁的基本原理:应该是 非必要,不加锁,不能无脑加锁

2.我们加入一个双重if

3.于是我们再进行优化 给instance加一个volatile

(这里可能存在内存可见性的风险,触不触发是未知的 加上volatile是一个更稳妥的做法)而加上volatile还有另一个用途:避免此处赋值操作的指令从排序

而我们的最终版本就是如此

2.阻塞队列  

   a)  队列分为很多种:普通队列,优先级队列,阻塞队列(带有阻塞功能,常用于后端开发)

   b)这个案例来详细讲讲阻塞队列:阻塞队列有两种状态:

     1.当队列满的时候,继续入队列,就会出现阻塞,阻塞到其他线程从队列中取走元素为止

      2.当队列空的时候,继续出队列,也会出现阻塞状态,阻塞到其他线程往队列中添加元素即可

 c)首先我们来了解两个概念

      1.解耦合:就是降低模块之间的耦合 耦合:就是两个系统关联很紧密 一个系统出现问题,另一个系统会受到较大的影响 如果在他们中间引入一个阻塞队列就能很好的解决上述的问题 ,而这也有缺陷:会使效率降低。

       2.削峰填谷:一个机器的硬件资源是有限的,服务器每次处理一个请求,都要消耗一定的硬件资源 而一个分布式系统中,经常会出现,有的机器承受压力更大,有的机器承受压力更小,当两个系统如图所示

 此时,如果A接收到一个请求,B也就需要立即处理这个请求,如果A能承受足够的压力 B能够承受的压力小的话,此时B可能就会崩溃

而假如我们添加一个阻塞队列,如果所示

 当外界的请求暴涨时,A的请求多了 A就会往请求中写入更多的数据,但是B仍然可以按照既定的节奏来处理,B就不至于崩溃 这时候队列就起到了一个缓冲的作用、(削峰)

而峰值很多时候只是暂时的,当峰值消退的时候,A收到的请求少了,B还是按照既定的节奏来处理,B也不至于太空闲 (填谷)

  d)java有三种阻塞队列

 第一行是基于链表的,第二行是基于堆实现的,第三行是基于数组的

Array这个版本速度更快,但前提是要知道有多少个元素,如果不知道有多少元素,使用Linked更合适

     a)对于BlockingQueue来说  offer poll 不带有阻塞功能,puttake带有阻塞功能

     b)基于阻塞队列写一个生产者消费者模型

package Thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class no4 {
    public static void main(String[] args) {
        //负责生产元素

        BlockingQueue<Integer> queue=new LinkedBlockingQueue<>();
        Thread t1=new Thread(()->{
            int count =0;
            while (true){
                try {
                    queue.put(count);
                    System.out.println("生产元素"+count);
                    count++;


                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //负责消费元素
        Thread t2=new Thread(()->{
            while (true){
                try {
                    Integer n =queue.take();
                    System.out.println("消费元素"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();
    }
}

d)这是java提供现有的阻塞队列 我们自己来编写代码实现阻塞队列

       1.首先我们先写出入队列的代码

      

 

  2. 然后我们写出出队列的代码

 3.我们现在写出了基础代码 因此我们需要考虑线程安全问题

    a)先给put和take进行加锁保证线程安全

b)除了线程安全问题 我们还需要考虑内存可见性问题 因此,我们还需加上volatile

4.实现阻塞

    a)当队列为满时 进行put会产生阻塞

 take的notify唤醒put的wait

    b)当队列为空是 ,进行take会产生阻塞

 

 put的notify唤醒take的wait

c)但是 以上代码仍然有概率数据会覆盖 因此 我们把if改为while进行再一次的判断

 

因此 我们完整的代码如图所示

package Thread;

class MyBlockingQueue {
    private String[] items = new String[1000];
     volatile private int head = 0;
     volatile private int tail = 0;
    //指向队列的尾部的下一次元素,队列的有效元素范围[head,tail)
     volatile private int size = 0;

   private Object locker=new Object();

    //元素个数
    //入队列
    public void put(String elem) throws InterruptedException {
        synchronized (locker) {
            while (size >= items.length) {
                locker.wait();

                //队列满了
                //return;
            }
            items[tail] = elem;
            tail++;
            if (tail >= items.length) {
                tail = 0;


            }
            size++;
            locker.notify();
            //用来唤醒队列为空的阻塞情况

        }
    }

    //出队列
    public String take() throws InterruptedException {
        synchronized (locker){
        if (size == 0) {
            //return null;
            locker.wait();
        }
        String elem = items[head];
        head++;
        if (head >= items.length) {
            head = 0;
        }
        size--;
        locker.notify();
        //使用这个notify来唤醒队列满的阻塞情况
        return elem;

    }
}



        }

public class no20 {
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue queue=new MyBlockingQueue();
//        queue.put("aaa");
//        queue.put("bbb");
//        queue.put("ccc");
//        String elem=queue.take();
        //创建两个线程代表生产者消费者
        Thread t1=new Thread(()->{
            int count=0;
            while(true){
                try {
                    queue.put(count+" ");
                    System.out.println("生产元素"+count);
                    count++;

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2=new Thread(()->{
           while (true){
               try {
                   String count=queue.take();
                   System.out.println("消费元素"+count);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });


        t1.start();
        t2.start();

    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值