多线程篇六

ops/2024/9/23 11:20:41/

多线程篇六

如笔者理解有误欢迎交流指正~⭐


什么是单例模式

单例模式是最常见的 设计模式.
顾名思义,单例模式指的就是单个实例的模式.(针对某些类只能使用一个对象的场景【如MySQL、JDBC、DataSource】)

设计模式

设计模式是针对某些问题场景而产生的处理问题的方法.(就跟你想吃早饭,可以选择自己做或者出去买或者蹭别人或者别的解决方法一样)
tips
单例模式是线程安全的,能保证某个类在程序中只存在唯一一份实例而不会创建出多个实例.

单例模式又分为饿汗和懒汉两种.

饿汉模式

创建的比较早,类加载时就创建出了.

java">class Singleton {private static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance() {return instance;}
}public class TestSingleton {public static void main(String[] args) {Singleton.getInstance();Singleton s = new Singleton();}
}

注意
1.将instance 设为静态成员,在Singleton类被加载的时候进行实例创建(类加载创建)
2.通过此方法获取new出来的实例,其他代码块后续想一直使用这个类(获取这个类唯一的实例),使用getInstance方法即可.
3.private Singleton() {} 是在设置私有构造方法,保证其它代码不能创建出新的对象.

img

懒汉模式

创建的比较迟,首次使用的时候才创建.

单线程版
java">class SingletonLazy {//先将引用初始化为null 不立即创建实例private static SingletonLazy instance = null;private static SingletonLazy getInstance() {if(instance == null) {instance = new SingletonLazy();}return instance;}private SingletonLazy() { }
}

注意
1.首次使用instance的时候才真正创建实例.(不调用就不创建)
2.第一次调用getInstance时,instance引用为null,进入if创建出的实例可以持续调用的实例.

对比

1.懒汉模式比饿汉模式效率更高.

2.饿汉模式更具线程安全,饿汉模式getInstance只进行读取,懒汉模式对数据既会读取数据又会修改数据.

线程安全问题发生在首次创建实例时,如果多个线程同时调用getInstance方法对变量进行修改就可能导致线程安全问题.
怎么解决呢?synchronized!

多线程版
java">class Singleton {private static Object locker = new Object();private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {synchronized(locker) {if(instance == null) {instance = new Singleton();}}return instance;}
}

问题来了😀.这么写后续每次调用getInstance都需要先加锁,但实际上懒汉模式线程安全问题只出现在new对象时,一但对象new出来后续多线程调用getInstance只有读操作了,就不存在线程安全问题了.【加锁就可能涉及到锁冲突一冲突就会引起阻塞和高性能无缘】
解决方案

java">class Singleton {private static Object locker = new Object();private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {//第一个if判定的是是否加锁(保证执行效率)synchronized(locker) {if(instance == null) {//第二个if判定的是是否要创建对象(保障线程安全)instance = new Singleton();}}}return instance;}
}

在外层再加一层if判断(如果instance为null,即为首次调用->是否需要加锁,非null->后续会调用->不用加锁)
但是又有惊喜来了!指令重排序!

java">instacnce = new Singleton();

这条语句执行有三个指令
1.申请一段内存空间
2.在内存上调用构造方法,创建出实例
3.把内存地址赋值给instance
前面给大家介绍过,这些指令正常情况下按顺序执行,但CPU 可以会自己进行优化打乱顺序.
怎么解决?
volatile关键字!(防止指令重排序)

java">class Singleton {private static Object locker = new Object();private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {//第一个if判定的是是否加锁(保证执行效率)synchronized(locker) {if(instance == null) {//第二个if判定的是是否要创建对象(保障线程安全)instance = new Singleton();}}}return instance;}
}
阻塞队列
什么是阻塞队列

阻塞队列是一种特殊的队列,遵守”先进先出”原则.【典型的生产者消费者模型】

特性

1.队列满的时候继续入队会阻塞,直到有其他线程从队列中取走元素.
2.队列空时继续出队也会阻塞,直到有其他线程王队列中插入元素.

生产者消费者模型

分布式系统在实际开发中经常涉及,核心是分开工作发挥效果.服务器整个功能的实现是由每个服务器单独负责一部分工作实现的,通过各个服务器之间的网络通信完成整个功能.
img
注意
1.上述的阻塞队列是基于对应数据结构实现的服务器程序,被部署到单独的主机上.整个系统的结构更复杂.
2.引入阻塞队列在A发送请求到B接收是有开销损耗的.

解耦合

阻塞队列能使生产者和消费者解耦合.
高考完的暑假想赚点小钱,你和你的朋友开始摆摊卖冰汤圆,每个人都有明确的分工.(是的我是大馋丫头)小A负责采购材料,小B负责制作,小C负责配送,你负责宣传和看城管.顾客是“消费者”,不需要关注你们作为“生产者”谁做了冰汤圆.有吃就行.

削峰填谷

阻塞队列相当于一个缓冲区,平衡了生产者和消费者之间的处理能力.
618大抢购,一分钟之内可能会产生数百万订单,服务器在同一时刻收到大量的支付请求,直接处理服务器受不了会崩溃,(一个请求耗费的资源少但积累量变产生质变,任何一种硬件资源达到瓶颈服务器都会寄)
这时候就是阻塞队列大显身手的时候,将请求都放到一个阻塞队列中,然后再由消费者线程慢慢来处理每个支付请求.

代码实现
java">public class TestCustomerAndProducer {public static void main(String[] args) {BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();Thread customer = new Thread(() -> {while(true) {try {int value = blockingQueue.take();System.out.println("Consumption element: " + value);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}, "customer");Thread producer = new Thread(() -> {Random r = new Random();while(true) {try {int num = r.nextInt(1000);System.out.println(" Production elements: " + num);blockingQueue.put(num);Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}}, "procedure");customer.start();producer.start();}
}
实现阻塞队列
java">import java.util.Random;public class BlockingQueue {public int[] elems = new int[2000];private volatile int size = 0;private volatile int head = 0;private volatile int tail = 0;private Object locker = new Object();public synchronized int getSize() {return size;}public void put(int value) throws InterruptedException{synchronized(locker){while(size >= elems.length) {//满 阻塞等待locker.wait();}elems[tail] = value;tail = (tail + 1) % elems.length;size++;//入队后唤醒locker.notify();}}public int take() throws InterruptedException {int ret = 0;synchronized(locker) {while(size <= 0) {//队列空继续阻塞locker.wait();}ret = elems[head];head = (head + 1) % elems.length;size--;//出队成功后唤醒locker.notify();}return ret;}
}

注意
1.使用循环队列实现(注意理解头指针和尾指针的变化)
2.put和take使用的是同一把锁,若队列被put满之后又唤醒了另一个阻塞的put就会出bug,加while判断,如果队列一直是慢的就不再被唤醒,保证安全性.

标准库中的阻塞队列

在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.
1.BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
2.put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
3.BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.

java">public class BlockingQueue {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>();//入队列queue.put("abc");//出队列.如果没有put直接take,会阻塞.String elem = queue.take();System.out.println(elem);}
}

未完待续🌟(●’◡’●)


http://www.ppmy.cn/ops/113539.html

相关文章

GoFly快速开发框架/Go语言封装的图像相似性比较插件使用说明

说明 图像相似性搜索应用广泛、除了使用搜索引擎搜索类似图片外&#xff0c;像淘宝可以让顾客直接拍照搜索类似的商品信息、应用在商品购物上&#xff0c;也可以应用物体识别比如拍图识花等领域。还有在调研图片鉴权的方案&#xff0c;通过一张图片和图片库中的图片进行比对&a…

HTML中的零宽字符

概述 零宽字符是一组在文本中没有可见宽度的字符,它们通常用于处理文本的布局、分隔和合成。下面是一些常见的零宽字符及其详细介绍: 详细介绍 零宽空格 (Zero Width Space, ZWSP) Unicode 码位: U+200BHTML 实体: ​ 或 ​用途: 用于防止文字自动换行。用于在文本中插入不…

flink的窗口

Flink的窗口是处理无界流数据的一种重要机制&#xff0c;它将无限的流数据切割成有限的、可管理的部分&#xff0c;以便进行聚合、计算和分析。以下是关于Flink窗口的简要介绍及其主要类型的区别&#xff1a; 一、Flink窗口概述 定义&#xff1a;窗口是Flink在处理流数据时用于…

苹果为什么不做折叠屏手机?

苹果为什么不做折叠屏手机&#xff1f;折叠屏手机在最近这些年里边&#xff0c;可以说是市场的一个主要在手机上的增长点。你像华W最近推出这个三折叠手机&#xff0c;引起了整个市场的轰动。 可是&#xff0c;为什么苹果到今天为止不为所动&#xff0c;还在那不停地在现在的这…

怎样把PPT上顽固的图标删了

例如&#xff1a; 解决&#xff1a; 首先打开下载好的PPT模板&#xff0c;然后在视图选项卡里面找到幻灯片母版。 进入幻灯片母版后&#xff0c;找到第一页母版页就会看到LOGO了&#xff0c;这时使用鼠标就可以选中删除啦。

CentOS7下安装Ruby3.2.4的实施路径

一、CentOS版本 [userzt ~]$ cat /etc/os-release NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_ID"7" PRETTY_NAME"CentOS Linux 7 (Core)" ANSI_COLOR"0;31" CPE…

tomcat项目报错org.apache.jasper.JasperException: java.lang.NullPointerException

现象&#xff1a; 访问tomcat项目报错&#xff0c;查看tomcat日志有报错 Sep 12, 2024 5:15:59 PM org.apache.catalina.core.StandardWrapperValve invoke SEVERE: Servlet.service() for servlet [jsp] in context with path [] threw exception java.lang.NullPointerExce…

在系统总线的数据线上,不可能传输的是()。

在系统总线的数据线上&#xff0c;不可能传输的是 A&#xff0e;指令 B&#xff0e;操作数 C&#xff0e;握手&#xff08;应答&#xff09;信号 D&#xff0e;中断类型号 答案&#xff1a;在系统总线的数据线上&#xff0c;不可能传输的是&#xff1a; C. 握手&#xff08;应…