JavaWeb语法四:多线程案例

news2024/11/17 3:37:19

目录

1.单例模式

1.1:饿汉模式

1.2:懒汉模式

 2.阻塞式队列

2.1:生产者消费者模型

2.2:阻塞队列的模拟实现

3.线程池

3.1:标准库中的线程池

3.2:模拟实现线程池


前言:前一篇我们讲了线程不安全的原因以及解决办法,接下来我们将讲多线程案例,进一步来了解线程。

1.单例模式

单例模式(单个实例,单个类,只能被创建一个实例。)是校招中最常考的设计模式。而设计模式是什么意思勒?

设计模式:就是针对一些典型的场景,给出一些典型的方案,就想棋谱一样。

1.1:饿汉模式

class singleton{
    private  static  singleton instance=new singleton();

    private singleton() {
    }
    public  static singleton getInstance(){
        return instance;
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        //singleton s=new singleton();//这里为啥报错了?
        singleton s1=singleton.getInstance();
        singleton s2=singleton.getInstance();
        System.out.println(s1==s2);//这个结果是true为啥了
    }
}

这个饿汉模式是怎么实现了只能创造一个实例,且实例是唯一的。

1.private static singleton instance=new singleton();

static 修饰的成员instance是类属性,相当于这个属性对应的内存空间在类对象中,而在这里类对象又是唯一的实例(是在类加载阶段,就把实例创建出来)这就让这个实例是唯一的。

类加载:运行一个java程序,就需要让java进程能够找到并读取对应的.class文件。就会读取文件内容并解析,构造成类对象,这一系列的过程就是类加载。

2.private singleton(){}:有private 修饰的构造方法是私有的,只能在singleton这个类进行使用,无法进行new 一个对象。

3. 只能调用getInstance 这个 方法来获得instance这个实例对象。所以是s1和s2是指向同一个实例对象。所以返回的true。


1.2:懒汉模式

class singletonLazy{
    private  static singletonLazy instance=null;
   public static singletonLazy getInstance(){
       if(instance==null){
           instance=new singletonLazy();
       }
       return  instance;
   }
   private singletonLazy(){
   }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        singletonLazy s1=singletonLazy.getInstance();
        singletonLazy s2=singletonLazy.getInstance();
        System.out.println(s1==s2);
    }
}

这个懒汉模式,在类加载的时候不创建实例,第一次使用的时候才创建实例。但这个是线程不安全的。那我们要了解一下,为啥是线程不安全的勒?

为啥是线程不安全的?

线程不安全是首次创建实例时,如果在多个线程中同时调用getInstance,就可能导致创建出多个实例。

 线程一调用com的时候,instance这个对象是 null,在进行创建instance这个实例的时候,线程二进行com的时候,那时候instance这个对象还是 null,也会进行创建instance这个实例。这样可能导致创建多个实例。

为了解决线程不安全。使用双重if判定。

给instance加上volatile。

 2.阻塞式队列

阻塞队列是一种特殊的队列,遵循"先进先出"的原则。

当队列满的时候,继续入队列就会阻塞,直到有其他线程从队列中取走元素。

当队列空的时候,继续出队列也会阻塞,直到其他线程往队列中插入元素。

阻塞队列的一个典型应用场景就是 "生产者消费者模型",这是一种非常典型的开发模型。

2.1:生产者消费者模型

优点一:能够让多个服务器之间解耦合。

 A 只需要和阻塞队列进行交互,不需要知道B是怎么运行的。哪怕B挂了,出现了问题,跟A也没有什么关系,A和B是陌生人。

B 只需要和阻塞队列进行交互,不需要知道A是怎么运行的,哪怕A挂了,出现了问题,跟B也没有什么关系,B和A是陌生人。

优点二:能过对于请求削峰填谷。

假如未使用生产者消费者模型

 

 A暴涨=>B暴涨

A作为入口服务器,计算量很轻,请求暴涨,问题不大,但B作为应用服务器,计算量可能很大,需要的系统资源也更多,如果请求更多,需要的资源进一步增加,如果主机的硬件不够,程序就会面临挂的风险。

A请求暴涨=>阻塞队列的请求暴涨。由于阻塞队列没啥计算量,就只是单纯的存个数据,就能抗住更大的压力,而B这边仍然按照原来的速度来消费数据,不会因为A的暴涨而引来暴涨,这样B就被保护的很好,就不会因为这种请求的波动而引起来崩溃。

”削峰“:请求不会一直暴涨下去,而带来的这种峰值也不会持续很久,就一阵,过去就恢复了。

”填谷":当请求很少的时候,B依然按照原来的频率来处理之前积压的数据。

2.2:阻塞队列的模拟实现

通过循环队列的方式方法来实现。

使用synchronized进行加锁控制。

put插入元素的时候,判定如果队列满了,就进行wait。

take 取出元素的时候,判定如果队列为空,就进行wait。

class  BlockingQueue {
    int[] queue = new int[3];
    int prev = 0;
    int tail = 0;
    int size = 0;
    Object lock=new Object();
    //入队列
    public void put (int value) throws InterruptedException {
        //判断队列是否为满
        synchronized (lock) {
            if (size >= queue.length) {
                lock.wait();//队列满了,进入阻塞队列
            }
            queue[tail] = value;
            size++;
            tail++;
            if (tail >= queue.length) {
                tail = 0;
            }
            //此处唤醒put队列为空的阻塞等待
            lock.notify();
        }
    }
    public Integer take() throws InterruptedException {
        //判断队列是否为空
        synchronized (lock) {
            if (size == 0) {
                lock.wait();//队列为空进入阻塞队列
            }
            int val = queue[prev];
            prev++;
            size--;
            if (prev >= queue.length) {
                prev = 0;
            }
            lock.notify();//此处唤醒put队列未满的阻塞队列
            return val;
        }
    }
}
public class ThreadDemo3 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue blockingQueue=new BlockingQueue();
        //生产者
        Thread t=new Thread(()->{
            int num=0;
            while(true){
                try {
                    System.out.println("生产了"+num);
                    blockingQueue.put(num);
                    num++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t1=new Thread(()->{
            try {
                while(true) {
                    int num = blockingQueue.take();
                    System.out.println("消费了" + num);
                    Thread.sleep(30);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        t1.start();
    }
}

标准库里面的阻塞队列

BlockingQueue<Integer> blockingQueue=new LinkedBlockingQueue<>();

3.线程池

把线程提前创建好,放到线程池中,后面需要用线程,直接从池子取,就不必从系统这边申请。线程用完,也不是还给系统,而是返回池子中,以备下次再用。

3.1:标准库中的线程池

 创建固定线程数的线程池

ExecutorService pool= Executors.newFixedThreadPool(10)

这里10是指定创建10个线程的线程池。

创建线程数目动态增长

ExecutorService pool=Executors.newCachedThreadPool();

创建只包含单个线程的线程池

ExecutorService pool=Executors.newSingleThreadExecutor();


3.2:模拟实现线程池

 核心操作为submit,江任务加入到线程池中。

使用Worker类描述一个工作线程,使用runable描述一个任务。

使用一个BlockingQueue组织所有的任务。

每一个woker线程要做的事情,不停的从blockqueue中取任务并执行。

指定一个线程池中最大的线程数当当前线程数超过这个最大值时,就不再新增线程。
 

public class MyThreadPool {
    //使用一个数据结构来组织一个任务。
    BlockingDeque<Runnable> deque=new LinkedBlockingDeque<>();
    static  class  worker extends Thread {
        private BlockingDeque<Runnable> deque = null;

        public worker(BlockingDeque<Runnable> deque) {
            this.deque = deque;
        }

        @Override
        public void run() {
            //循环去线程池中拿出任务
            while (true) {
                Runnable runnable = null;
                try {
                    runnable = deque.take();
                    runnable.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    //创建一个数据结构来组织线程
    List<Thread>works=new ArrayList<>();
//在构造方法中,就这些线程放到上述的线程池中
    public MyThreadPool(int n) {
        for(int i=0;i<n;i++){
            worker worker=new worker(deque);
            worker.start();
            works.add(worker);
        }
    }
    public  void submit(Runnable runnable){
        try {
            deque.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        MyThreadPool pool=new MyThreadPool(10);
        for (int i=0;i<10;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
        }
    }
}

总结:

以上就是我总结的多线程案例,若有错误或者有不对的,请各位铁子留言纠错,若感觉不错,请一键三连。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP UI5 里 FlexBox 的使用方法

ScrollContainer 的使用方式&#xff1a; ScrollContainer 是一个控件&#xff0c;可以在有限的屏幕区域内显示任意内容&#xff0c;并提供滚动以使所有内容都可访问。注意&#xff0c;为了避免影响用户使用体验&#xff0c;不要嵌套沿相同方向滚动的滚动区域。例如&#xff…

【LaTex】基础语法框架快速入门教程——Tex live+TexStudio简要安装及使用教程

0. 引言 LaTeX对于论文排版有着巨大的便利&#xff0c;并且对于参考文献的引用也十分方便&#xff0c;不会出现使用word引用参考文献一旦更改文献引用顺序&#xff0c;就必须全部改编号的情况。这里记录一下如何从0开始学习使用LaTeX书写论文。 1. 软件安装&环境配置 1.…

Qt中实例化一个函数变量时加不加括号的区别,以及括号中的this的使用

一、设计一个测试小程序 废话不多说&#xff0c;直接上代码。 main.h函数就不多说了&#xff0c;没改动。直接上mainwindow.h&#xff0c;也没改动。看mainwindow.cpp的内容。 #include "mainwindow.h" #include "ui_mainwindow.h" #include "test.…

机器学习算法基础——KNN算法

KNN (K-Nearest Neighbor)–K近邻分类算法 • 为了判断未知实例的类别&#xff0c;以所有已知类别的实例作为参照选择参数K • 计算未知实例与所有已知实例的距离 • 选择最近K个已知实例 • 根据少数服从多数的投票法则(majority-voting)&#xff0c;让未知实例归类为K个最邻…

Zerobot僵尸网络出现了新的漏洞利用和功能

©网络研究院 Zerobot DDoS僵尸网络已经获得了重大更新&#xff0c;扩展了其针对更多互联网连接设备和扩展网络的能力。 微软威胁情报中心 (MSTIC)正在以DEV-1061的名称跟踪持续的威胁&#xff0c;名称为未知、新兴或发展中的活动群集。 本月早些时候&#xff0c;Fort…

2022年山东省职业院校技能大赛中职组“网络安全”赛项规程

2022年山东省职业院校技能大赛中职组“网络安全”赛项规程一、赛项名称赛项名称&#xff1a;网络安全英文名称&#xff1a;Cyber Security赛项组别&#xff1a;中职组赛项类别:电子与信息类二、竞赛目的网络空间已经成为陆、海、空、天之后的第五大主权领域空间&#xff0c;习总…

SpringCloud 网关组件 Zuul-1.0 原理深度解析

为什么要使用网关&#xff1f; 在当下流行的微服务架构中&#xff0c;面对多端应用时我们往往会做前后端分离&#xff1a;如前端分成 APP 端、网页端、小程序端等&#xff0c;使用 Vue 等流行的前端框架交给前端团队负责实现&#xff1b;后端拆分成若干微服务&#xff0c;分别…

k8s使用glusterfs(静态供给、动态供给)、glusterfs的安装与使用

目录前言主机准备配置主机名、关闭防火墙、关闭selinux挂载磁盘安装glusterfs服务端glusterfs的端口分布式集群的结构组成glusterfs集群创建存储卷启动卷k8s使用glusterfs作为后端存储&#xff08;静态供给glusterfs存储&#xff09;恢复初始化环境安装Heketi 服务&#xff08;…

如何快速读懂开源代码?

文章目录**RUN起来****调试****把控关键数据结构和函数****从小的开始****关注一个模块****工具****一、阅读开源代码存在的一些误区**二、阅读代码的心态**三、阅读源码与**辅助材料**四、如何阅读开源代码****《gdb 高级调试实战教程》电子书下载链接&#xff1a;**1 下载 Ng…

ASP.NET开源版MES加工装配模拟系统源码/WinForm工厂加工装配系统源码/流程工序管理

一、源码描述 本系统用户大学机械科上位机加工装配模拟实验&#xff0c;目前正常用于实验当中。环境&#xff1a;VS2010(C# .NET4.0,多层结构)、sqlserver2008 r2 &#xff1b;Winform;使用到RFID读写器&#xff08;设备是可以变更的&#xff0c;修改RFID.Library项目的…

Mycat2(一)简介、分库分表概念

文章目录mycat是什么&#xff1b;为什么要用&#xff1b;mycat的作用原理分库分表的意义数据库优化的层次&#xff1a;数据切分的方式&#xff0c;带来的问题和解决方案分库分表带来的问题mycat的特性与详细配置含义mycat2与mycat1.6区别mycat2映射模型多数据源解决方案mycat核…

JavaScript控制元素(标签)的显示与隐藏

使用JavaScript有多种方式来隐藏元素&#xff1a; 方式一、使用HTML 的hidden 属性&#xff0c;隐藏后不占用原来的位置 hidden 属性是一个 Boolean 类型的值&#xff0c;如果想要隐藏元素&#xff0c;就将值设置为 true&#xff0c;否则就将值设置为false 【HTML hidden 属性…

【STM32+cubemx】0030 HAL库开发:DDS芯片AD9833实现简单的波形发生器

大家好&#xff0c;我是学电子的小白白&#xff0c;今天带大家了解一款波形发生器芯片——AD9833。 AD9833是AD公司出品的一款DDS波形发生器&#xff0c;能够产生正弦波、三角波和方波输出。 1&#xff09;什么是DDS 通俗来讲&#xff0c;DDS是一种把波形预先存储在芯片内部的…

H5对接NSS主扫遇到的一些问题

需要对接以下这些钱包&#xff1a; WXPAY(微信)ALIPAY&#xff08;支付宝&#xff09;LINEPAY&#xff08;linePay&#xff09;PAY_PAY (PayPay)RAKUTEN_PAY&#xff08;乐天&#xff09;MER_PAY(MerPay)AU_PAY(auPay)DOCOMO_PAY&#xff08;Dpay&#xff09;判断钱包类型 这…

注册中心Nacos

Nacos是Spring Cloud Alibaba提供的一个软件 这个软件主要具有注册中心和配置中心(课程最后讲解)的功能 我们先学习它注册中心的功能 微服务中所有项目都必须注册到注册中心才能成为微服务的一部分 注册中心和企业中的人力资源管理部门有相似 当前微服务项目中所有的模块,在…

用户购车旅程转变下,深度运营中的难点痛点如何突破?

在数字互联的营销环境下&#xff0c;消费者的购车旅程和用户行为发生了极大的改变&#xff0c;线上决策比重逐渐增强&#xff0c;到店决策周期越来越短&#xff0c;消费者可以在线完成70%-80%的车型甄选和决策。在这种环境下&#xff0c;未来如何实现更高效的用户运营和快速的销…

Python基础(十八):学员管理系统应用

文章目录 学员管理系统应用 一、系统简介 二、步骤分析 三、需求实现 1、显示功能界面 2、用户输入序号&#xff0c;选择功能 3、根据用户选择&#xff0c;执行不同的功能 4、定义不同功能的函数 学员管理系统应用 一、系统简介 需求&#xff1a;进入系统显示系统功能…

汽车租赁小程序源码 上门取车

小程序端&#xff1a; 首页、订单、我的 上门取送&#xff1a;仅限上门取送范围、到店取还&#xff1a;为您推荐最近的门店 套餐1、领优惠券、签到积分、限时活动、车型推荐 订单&#xff1a;订单中心、短租订单、长租订单 个人中心&#xff1a;我的优惠、租车券、优惠券、…

Android入门第55天-在Android里使用OKHttp组件访问网络资源

简介 今天的课程开始进入高级课程类了&#xff0c;我们要开始接触网络协议、设备等领域编程了。在今天的课程里我们会使用OKHttp组件来访问网络资源而不是使用Android自带的URLConnection。一个是OKHttp组件更方便二个是OKHttp组件本身就带有异步回调功能。 下面就进入课程。…

《北京市数字经济促进条例》图解来了

《北京市数字经济促进条例》 2023/1/1 二十大对建设数字经济作出重要部署。党的二十大报告中指出&#xff0c;“加快建设网络强国、数字中国”、“加快发展数字经济&#xff0c;促进数字经济和实体经济深度融合&#xff0c;打造具有国际竞争力的数字产业集群”。习总书记强调…